├── tests ├── __init__.py ├── test_config.py └── test_api.py ├── ami_deprecation_tool ├── __init__.py ├── configmodels.py ├── cli.py └── api.py ├── .gitignore ├── renovate.json ├── SECURITY.md ├── tox.ini ├── .github └── workflows │ └── pr.yaml ├── pyproject.toml ├── snap └── snapcraft.yaml ├── README.rst └── poetry.lock /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ami_deprecation_tool/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | policy.yaml 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "packageRules": [ 7 | { 8 | "groupName": "all non-major dependencies", 9 | "groupSlug": "all-minor-patch", 10 | "matchPackageNames": [ 11 | "*" 12 | ], 13 | "matchUpdateTypes": [ 14 | "minor", 15 | "patch" 16 | ] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting a vulnerability 2 | To report a security issue, file a [Private Security Report](https://github.com/Canonical/ami-deprecation-tool/security/advisories/new) 3 | with a description of the issue, the steps you took to create the issue, 4 | affected versions, and, if known, mitigations for the issue. 5 | The [Ubuntu Security disclosure and embargo policy](https://ubuntu.com/security/disclosure-policy) 6 | contains more information about what you can expect when you contact us and what we expect from you. 7 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | isolated_build = true 3 | env_list = lint,py3,test 4 | minversion = 4.10.0 5 | 6 | [testenv] 7 | skip_install = true 8 | deps = 9 | poetry 10 | allowlist_externals = poetry 11 | commands_pre = poetry install 12 | package = wheel 13 | wheel_build_env = .pkg 14 | 15 | [testenv:test] 16 | description = run the tests with pytest 17 | commands = 18 | poetry run poe test 19 | 20 | [testenv:lint] 21 | description = run linters 22 | commands = 23 | poetry run poe lint 24 | 25 | [testenv:fix] 26 | description = run formatters 27 | commands = 28 | poetry run poe fix 29 | 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/pr.yaml: -------------------------------------------------------------------------------- 1 | name: testing 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | tox: 11 | runs-on: [self-hosted, linux, AMD64, medium, noble] 12 | strategy: 13 | matrix: 14 | python: ["3.10", "3.11", "3.12", "3.13"] 15 | steps: 16 | - name: Setup Python 17 | uses: actions/setup-python@v5 18 | with: 19 | python-version: ${{ matrix.python }} 20 | - name: Install tox and any other packages 21 | run: | 22 | pip3 install tox 23 | - uses: actions/checkout@v4 24 | - name: Run tox 25 | run: tox 26 | snap: 27 | runs-on: [self-hosted, linux, AMD64, medium, noble] 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: snapcore/action-build@v1 31 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "ami-deprecation-tool" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Ryan Hill "] 6 | readme = "README.rst" 7 | 8 | [tool.poetry.scripts] 9 | deprecate-amis = "ami_deprecation_tool.cli:deprecate" 10 | 11 | [tool.poetry.dependencies] 12 | python = "^3.10" 13 | pyyaml = "^6.0.2" 14 | boto3 = "^1.35.43" 15 | click = "^8.1.7" 16 | pydantic = "^2.9.2" 17 | boto3-stubs = {extras = ["essential"], version = "^1.35.40"} 18 | 19 | [tool.poetry.group.dev.dependencies] 20 | ruff = "^0.14.0" 21 | pytest = "^8.3.3" 22 | poethepoet = "^0.37.0" 23 | mypy = "^1.13.0" 24 | types-pyyaml = "^6.0.12.20240917" 25 | 26 | [tool.ruff] 27 | line-length = 120 28 | lint.extend-select = ["I"] 29 | 30 | [tool.poe.tasks] 31 | test = "pytest" 32 | type-check = "mypy ami_deprecation_tool" 33 | check = "ruff check" 34 | check-fix = "ruff check --fix" 35 | format-dry = "ruff format --diff" 36 | format = "ruff format" 37 | fix = ["check-fix", "format"] 38 | lint = ["check", "type-check", "format-dry"] 39 | 40 | [build-system] 41 | requires = ["poetry-core"] 42 | build-backend = "poetry.core.masonry.api" 43 | -------------------------------------------------------------------------------- /tests/test_config.py: -------------------------------------------------------------------------------- 1 | # from unittest.mock import MagicMock, call, patch 2 | 3 | import pytest 4 | 5 | from ami_deprecation_tool import configmodels 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "config,expected_exec_users,expected_deprecated,expected_disabled", 10 | [ 11 | ({}, [], False, False), 12 | ({"include_deprecated": True, "include_disabled": False}, [], True, False), 13 | ({"include_deprecated": False, "include_disabled": True}, [], False, True), 14 | ({"executable_users": ["all"]}, ["all"], False, False), 15 | ({"executable_users": ["123", "456", "789"]}, ["123", "456", "789"], False, False), 16 | ({"executable_users": ["self"], "include_deprecated": True, "include_disabled": True}, ["self"], True, True), 17 | ], 18 | ) 19 | def test_config_options( 20 | config, 21 | expected_exec_users, 22 | expected_deprecated, 23 | expected_disabled, 24 | ): 25 | options = configmodels.ConfigOptionsModel(**config) 26 | assert options.executable_users == expected_exec_users 27 | assert options.include_disabled == expected_disabled 28 | assert options.include_deprecated == expected_deprecated 29 | -------------------------------------------------------------------------------- /ami_deprecation_tool/configmodels.py: -------------------------------------------------------------------------------- 1 | from typing import Literal 2 | 3 | from pydantic import BaseModel, Field 4 | 5 | 6 | class ConfigPolicyModel(BaseModel): 7 | """ 8 | Deprecation policy Configuration 9 | """ 10 | 11 | action: Literal["delete", "deprecate"] = Field( 12 | description="The action to be performed on AMIs that are out of policy" 13 | ) 14 | keep: int = Field(description="The number of AMIs to exempt from the policy") 15 | keep_days: int = Field(description="How many days to exempt AMIs from the policy", default=0) 16 | 17 | 18 | class ConfigOptionsModel(BaseModel): 19 | """ 20 | Tool configuration model 21 | """ 22 | 23 | executable_users: list[str] = Field( 24 | default=[], 25 | description=( 26 | "List of accounts with run permissions. Special values 'self' and" 27 | " 'all' are permitted. An empty list will allow all" 28 | " configurations." 29 | ), 30 | ) 31 | include_deprecated: bool = Field(default=False, description=("Include deprecated images in policy application")) 32 | include_disabled: bool = Field(default=False, description=("Include disabled images in policy application")) 33 | 34 | 35 | class ConfigModel(BaseModel): 36 | """ 37 | The base model for configuration 38 | """ 39 | 40 | options: ConfigOptionsModel 41 | images: dict[str, ConfigPolicyModel] 42 | -------------------------------------------------------------------------------- /snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: ami-deprecation-tool 2 | base: core22 3 | version: git 4 | summary: A tool to aid in the deprecation of images across AWS 5 | description: | 6 | A tool to aid in the deprecation of images across AWS. 7 | 8 | Credentials in ~/.aws/ need to be configured to be 9 | able to use this tool. 10 | 11 | Note: this snap is provided and maintained by Canonical! 12 | (**not** by Amazon/AWS) 13 | 14 | confinement: strict 15 | license: GPL-3.0 16 | architectures: 17 | - build-on: [amd64] 18 | - build-on: [arm64] 19 | 20 | plugs: 21 | dot-aws-config: 22 | interface: personal-files 23 | read: 24 | - $HOME/.aws/config 25 | dot-aws-credentials: 26 | interface: personal-files 27 | read: 28 | - $HOME/.aws/credentials 29 | dot-aws-models: 30 | interface: personal-files 31 | read: 32 | - $HOME/.aws/models 33 | 34 | apps: 35 | ami-deprecation-tool: 36 | command: bin/deprecate-amis 37 | environment: 38 | PYTHONPATH: $SNAP/lib/python3.12/site-packages 39 | # need to set $HOME to the real HOME here because this is a strict snap 40 | # and the creds for aws are in $HOME/.aws 41 | HOME: $SNAP_REAL_HOME 42 | plugs: 43 | - home 44 | - network 45 | - dot-aws-config 46 | - dot-aws-credentials 47 | - dot-aws-models 48 | 49 | parts: 50 | ami-deprecation-tool: 51 | plugin: python 52 | source: . 53 | stage-packages: 54 | - python3-poetry 55 | -------------------------------------------------------------------------------- /ami_deprecation_tool/cli.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | 4 | import click 5 | import yaml 6 | from botocore.exceptions import ClientError 7 | from pydantic import ValidationError 8 | 9 | from . import api 10 | from .configmodels import ConfigModel 11 | 12 | 13 | @click.command() 14 | @click.option( 15 | "-p", 16 | "--policy", 17 | "policy_path", 18 | required=True, 19 | type=click.Path(exists=True, dir_okay=False), 20 | help="path to yaml config file.", 21 | ) 22 | @click.option( 23 | "-v", 24 | "log_level", 25 | count=True, 26 | help="Set log verbosity. The default log level is WARNING. '-v' will set to INFO and '-vv' will set to DEBUG", 27 | ) 28 | @click.option("-o", "--output-actions", type=str, help="yaml file to write action log to") 29 | @click.option( 30 | "--dry-run/--no-dry-run", 31 | "dry_run", 32 | default=True, 33 | help="Prevent deprecation, only log intended actions (default=True)", 34 | ) 35 | def deprecate(policy_path, log_level, output_actions, dry_run): 36 | _setup_logging(log_level) 37 | config = _load_policy(policy_path) 38 | try: 39 | actions = api.deprecate(config, dry_run) 40 | if output_actions: 41 | with open(output_actions, "w") as fh: 42 | yaml.dump(actions, fh) 43 | except ClientError as e: 44 | sys.exit(e) 45 | 46 | 47 | def _load_policy(policy_path: str) -> ConfigModel: 48 | with open(policy_path) as fh: 49 | config = yaml.safe_load(fh) 50 | try: 51 | return ConfigModel(**config["ami-deprecation-tool"]) 52 | except ValidationError as e: 53 | sys.exit(e.json()) 54 | 55 | 56 | def _setup_logging(log_level: int) -> None: 57 | root_logger = logging.getLogger() 58 | log_formatter = logging.Formatter("%(asctime)s:%(name)s:%(levelname)s:%(message)s") 59 | 60 | # Set log level by verbosity (stop at 10/DEBUG to avoid accidentally using NOTSET) 61 | root_logger.setLevel(max(30 - (10 * log_level), 10)) 62 | 63 | console_handler = logging.StreamHandler() 64 | console_handler.setFormatter(log_formatter) 65 | 66 | root_logger.addHandler(console_handler) 67 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ami_deprecation_tool 2 | -------------------- 3 | 4 | ``ami_deprecation_tool`` can be used to apply a deprecation policy consistently across all regions of an account. 5 | 6 | Usage 7 | ===== 8 | 9 | The most simple usage of the tool is ``deprecate-amis -p policy.yaml --no-dry-run``. See below for a sample policy. 10 | 11 | Use ``deprecate-amis --help`` for a full list of options 12 | 13 | **Note: To avoid accidentally destructive behavior, 'dry-run' is the default behavior and --no-dry-run must be explicitly used** 14 | 15 | Policy Definition 16 | ================= 17 | 18 | .. code-block:: yaml 19 | 20 | ami-deprecation-tool: 21 | options: 22 | executable_users: 23 | - all # public images only 24 | include_deprecated: false 25 | include_disabled: false 26 | images: 27 | some/image/path/image-A-$serial: 28 | action: delete 29 | keep: 1 30 | some/image/path/image-B-$serial: 31 | action: deprecate 32 | keep: 3 33 | some/image/path/image-C-$serial: 34 | action: deprecate 35 | keep: 3 36 | keep_days: 90 37 | 38 | In the above example, ``some/image/path/image-A-$serial`` will find all images across all regions (owned by the current user) matching ``some/image/path/image-A-*`` where serial is replaced with a wildcard. These images will then be sorted by whatever matches in the place of $serial. The policy defined for this image is ``{action: delete, keep 1}`` meaning delete/deregister all except the latest image as defined by the sorted serials. 39 | 40 | The second image (``some/image/path/image-B-$serial``) has a policy of ``{action: deprecate, keep 3}``. Rather than deregistering the AMIs, all but the latest three will be scheduled for deprecation 1 minute in the future. These images will not be visible in the browser and will only show in API results if the caller specifies they are searching for deprecated AMIs 41 | 42 | The third image (``some/image/path/image-C-$serial``) has a policy of ``{action: deprecate, keep 3, keep_days 90}``. The three most recent images will not be scheduled for deprecation (like in the previous example). In addition, any image less than 3 months old, will also not be scheduled for deprecation. 43 | 44 | **Note: $serial is assumed to be consistently sortable using normal alphanumeric sorting** 45 | 46 | `executable_users` is a list of of accounts that can execute the images to be considered. It can include two special values, `self` and `all` where self is strictly private iamges and `all` which is all public AMIs. These values are passed directly to the AWS api and as such any of [their documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2/client/describe_images.html) on the field applies. 47 | -------------------------------------------------------------------------------- /tests/test_api.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from datetime import datetime, timedelta 3 | from unittest.mock import MagicMock, call, patch 4 | 5 | import pytest 6 | 7 | from ami_deprecation_tool import api, configmodels 8 | 9 | ONE_MONTH_AGO = datetime.now() - timedelta(days=30) 10 | SIX_MONTHS_AGO = datetime.now() - timedelta(days=180) 11 | 12 | 13 | def mk_image(image_id, name, date): 14 | return {"ImageId": image_id, "Name": name, "CreationDate": date} 15 | 16 | 17 | def mk_reg_img(region, image_id, date): 18 | return api.RegionImageContainer(region=region, image_id=image_id, creation_date=date, snapshots=[]) 19 | 20 | 21 | def make_region_images( 22 | image_count_expired: int, image_count_unexpired: int, missing: list[int] = [] 23 | ) -> dict[str, list[dict]]: 24 | """ 25 | Generate a dict shaped like boto3.describe_images with a list of images. 26 | :param image_count_expired: number of images to create with an old creation date 27 | :param image_count_unexpired: number of images to create with a new creation date 28 | :param image_count_expired: list of ami suffixes (like ["111","112","113"]) 29 | :param missing: optional list of ids to skip (simulate images absent in this region) 30 | """ 31 | 32 | images = [] 33 | 34 | for id_ in range(1, image_count_expired + image_count_unexpired + 1): 35 | if id_ in missing: 36 | continue 37 | creation_date = SIX_MONTHS_AGO if id_ < image_count_expired else ONE_MONTH_AGO 38 | images.append(mk_image(f"ami-{110 + id_}", f"Image-2025{100 + id_:04d}", creation_date)) 39 | 40 | return {"Images": images} 41 | 42 | 43 | @patch("ami_deprecation_tool.api._get_all_regions") 44 | @patch("ami_deprecation_tool.api.boto3") 45 | def test_deprecate_region_iteration(mock_boto, mock_get_all_regions): 46 | regions = ["region-1", "region-2", "region-3"] 47 | mock_get_all_regions.return_value = regions 48 | 49 | config = configmodels.ConfigModel(**{"images": {}, "options": {}}) 50 | 51 | api.deprecate(config, True) 52 | 53 | mock_boto.client.assert_has_calls( 54 | [ 55 | call("ec2"), 56 | *[call("ec2", region_name=r) for r in regions], 57 | ] 58 | ) 59 | 60 | 61 | @patch("ami_deprecation_tool.api.boto3") 62 | def test_get_images(mock_boto): 63 | mock_client = mock_boto.client.return_value 64 | mock_client.describe_images.return_value = { 65 | "Images": [ 66 | mk_image("125", "image-1-125", ONE_MONTH_AGO), 67 | mk_image("124", "image-1-124", ONE_MONTH_AGO), 68 | mk_image("126", "image-1-126", ONE_MONTH_AGO), 69 | mk_image("123", "image-1-123", ONE_MONTH_AGO), 70 | ] 71 | } 72 | 73 | mock_options = MagicMock() 74 | result = api._get_images(mock_client, "image-1-$serial", mock_options) 75 | 76 | assert result == [ 77 | mk_image("126", "image-1-126", ONE_MONTH_AGO), 78 | mk_image("125", "image-1-125", ONE_MONTH_AGO), 79 | mk_image("124", "image-1-124", ONE_MONTH_AGO), 80 | mk_image("123", "image-1-123", ONE_MONTH_AGO), 81 | ] 82 | 83 | 84 | @pytest.mark.parametrize( 85 | "options_dict", 86 | [ 87 | {}, 88 | {"include_disabled": False, "include_deprecated": False, "executable_users": ["all"]}, 89 | {"include_disabled": True, "include_deprecated": True, "executable_users": ["all"]}, 90 | ], 91 | ) 92 | @patch("ami_deprecation_tool.api.boto3") 93 | def test_get_images_options(mock_boto, options_dict): 94 | mock_client = mock_boto.client.return_value 95 | options = configmodels.ConfigOptionsModel(**options_dict) 96 | image_name = "image-name" 97 | future_deprecation_time = (datetime.now() + timedelta(minutes=5)).isoformat() 98 | 99 | mock_images = { 100 | "Images": [ 101 | {"Name": "image-name-1"}, 102 | {"Name": "image-name-2", "DeprecationTime": None}, 103 | {"Name": "image-name-3", "DeprecationTime": ""}, 104 | {"Name": "image-name-4", "DeprecationTime": future_deprecation_time}, 105 | {"Name": "image-name-4", "DeprecationTime": "2025-01-01T00:00:00Z"}, 106 | ] 107 | } 108 | mock_client.describe_images.return_value = mock_images 109 | 110 | images = api._get_images(mock_client, image_name, options) 111 | 112 | mock_client.describe_images.assert_called_once_with( 113 | Owners=["self"], 114 | IncludeDisabled=options.include_disabled, 115 | Filters=[{"Name": "name", "Values": [image_name]}], 116 | ExecutableUsers=options.executable_users, 117 | ) 118 | 119 | if options.include_deprecated: 120 | assert len(images) == 5 121 | else: 122 | assert len(images) == 4 123 | 124 | 125 | @patch("ami_deprecation_tool.api._delete_images") 126 | @patch("ami_deprecation_tool.api._deprecate_images") 127 | @patch("ami_deprecation_tool.api.boto3") 128 | def test_apply_policy(mock_boto, mock_deprecate_images, mock_delete_images): 129 | mock_client = mock_boto.client.return_value 130 | region_images = { 131 | "image-20250101": [ 132 | mk_reg_img("region-1", "ami-111", ONE_MONTH_AGO), 133 | mk_reg_img("region-2", "ami-112", ONE_MONTH_AGO), 134 | ], 135 | "image-20250201": [mk_reg_img("region-2", "ami-113", ONE_MONTH_AGO)], 136 | "image-20250301": [ 137 | mk_reg_img("region-1", "ami-211", ONE_MONTH_AGO), 138 | mk_reg_img("region-2", "ami-212", ONE_MONTH_AGO), 139 | ], 140 | "image-20250401": [ 141 | mk_reg_img("region-1", "ami-311", ONE_MONTH_AGO), 142 | mk_reg_img("region-2", "ami-312", ONE_MONTH_AGO), 143 | ], 144 | "image-20250501": [ 145 | mk_reg_img("region-1", "ami-411", ONE_MONTH_AGO), 146 | mk_reg_img("region-2", "ami-412", ONE_MONTH_AGO), 147 | ], 148 | "image-20250601": [mk_reg_img("region-2", "ami-413", ONE_MONTH_AGO)], 149 | } 150 | region_clients = {"region-1": mock_client, "region-2": mock_client} 151 | 152 | policy = configmodels.ConfigPolicyModel(**{"keep": 3, "action": "delete"}) 153 | api._apply_deprecation_policy(region_images.copy(), region_clients, policy, True) 154 | mock_delete_images.assert_called_once_with( 155 | True, 156 | region_clients, 157 | { 158 | "image-20250101": [ 159 | mk_reg_img("region-1", "ami-111", ONE_MONTH_AGO), 160 | mk_reg_img("region-2", "ami-112", ONE_MONTH_AGO), 161 | ], 162 | "image-20250201": [mk_reg_img("region-2", "ami-113", ONE_MONTH_AGO)], 163 | }, 164 | ) 165 | 166 | policy = configmodels.ConfigPolicyModel(**{"keep": 1, "action": "deprecate"}) 167 | api._apply_deprecation_policy(region_images.copy(), region_clients, policy, True) 168 | mock_deprecate_images.assert_called_once_with( 169 | True, 170 | region_clients, 171 | { 172 | "image-20250101": [ 173 | mk_reg_img("region-1", "ami-111", ONE_MONTH_AGO), 174 | mk_reg_img("region-2", "ami-112", ONE_MONTH_AGO), 175 | ], 176 | "image-20250201": [mk_reg_img("region-2", "ami-113", ONE_MONTH_AGO)], 177 | "image-20250301": [ 178 | mk_reg_img("region-1", "ami-211", ONE_MONTH_AGO), 179 | mk_reg_img("region-2", "ami-212", ONE_MONTH_AGO), 180 | ], 181 | "image-20250401": [ 182 | mk_reg_img("region-1", "ami-311", ONE_MONTH_AGO), 183 | mk_reg_img("region-2", "ami-312", ONE_MONTH_AGO), 184 | ], 185 | }, 186 | ) 187 | 188 | 189 | @patch("ami_deprecation_tool.api._perform_operation") 190 | @patch("ami_deprecation_tool.api.boto3") 191 | def test_snapshot_deleted_if_not_used(mock_boto, mock_perform_operation): 192 | mock_client = mock_boto.client.return_value 193 | mock_client.describe_images.return_value = {"Images": []} 194 | api._delete_snapshot(mock_client, "snapshot_id", True) 195 | mock_perform_operation.assert_called_once() 196 | 197 | 198 | @patch("ami_deprecation_tool.api._perform_operation") 199 | @patch("ami_deprecation_tool.api.boto3") 200 | def test_snapshot_skipped_if_in_use(mock_boto, mock_perform_operation): 201 | mock_client = mock_boto.client.return_value 202 | mock_client.describe_images.return_value = {"Images": [{"ImageId": "ami-123"}]} 203 | api._delete_snapshot(mock_client, "snapshot_id", True) 204 | mock_perform_operation.assert_not_called() 205 | 206 | 207 | def expect(delete, deprecate, keep, skip, policy): 208 | return { 209 | "image-20250101": api.Actions( 210 | images=api.ActionImages( 211 | delete=delete, 212 | deprecate=deprecate, 213 | keep=keep, 214 | skip=skip, 215 | ), 216 | policy=policy, 217 | ) 218 | } 219 | 220 | 221 | @dataclass(frozen=True) 222 | class Scenario: 223 | # ---- inputs ---- 224 | region1: dict 225 | region2: dict 226 | policy: dict 227 | # ---- expected outputs ---- 228 | delete: list[str] 229 | deprecate: list[str] 230 | keep: list[str] 231 | skip: list[str] 232 | 233 | 234 | SCENARIOS: dict[str, Scenario] = { 235 | # 1. test_action_output 236 | "action_output": Scenario( 237 | region1=make_region_images(image_count_expired=0, image_count_unexpired=4), 238 | region2=make_region_images(image_count_expired=0, image_count_unexpired=4, missing=[3]), 239 | policy={"action": "deprecate", "keep": 2, "keep_days": 0}, 240 | delete=[], 241 | deprecate=["Image-20250101"], 242 | keep=["Image-20250104", "Image-20250102"], 243 | skip=["Image-20250103"], 244 | ), 245 | # 2. test_deprecate_images_past_expiration”) 246 | # Some images expired, only keep within keep budget 247 | "past_expiration": Scenario( 248 | region1=make_region_images(image_count_expired=5, image_count_unexpired=1), 249 | region2=make_region_images(image_count_expired=5, image_count_unexpired=1, missing=[4]), 250 | policy={"action": "deprecate", "keep": 3, "keep_days": 90}, 251 | delete=[], 252 | deprecate=["Image-20250101", "Image-20250102"], 253 | keep=["Image-20250106", "Image-20250105", "Image-20250103"], 254 | skip=["Image-20250104"], 255 | ), 256 | # 3. test_keep_past_expiration 257 | # All images expired but keep-budget still applies 258 | "keep_past_expiration": Scenario( 259 | region1=make_region_images(image_count_expired=6, image_count_unexpired=0), 260 | region2=make_region_images(image_count_expired=6, image_count_unexpired=0, missing=[4]), 261 | policy={"action": "deprecate", "keep": 3, "keep_days": 90}, 262 | delete=[], 263 | deprecate=["Image-20250101", "Image-20250102"], 264 | keep=["Image-20250106", "Image-20250105", "Image-20250103"], 265 | skip=["Image-20250104"], 266 | ), 267 | # 4. test_keep_unexpired 268 | # No image expired, so nothing removed 269 | "unexpired": Scenario( 270 | region1=make_region_images(image_count_expired=0, image_count_unexpired=6), 271 | region2=make_region_images(image_count_expired=0, image_count_unexpired=6, missing=[4]), 272 | policy={"action": "deprecate", "keep": 3, "keep_days": 90}, 273 | delete=[], 274 | deprecate=[], 275 | keep=[ 276 | "Image-20250106", 277 | "Image-20250105", 278 | "Image-20250103", 279 | "Image-20250102", 280 | "Image-20250101", 281 | ], 282 | skip=["Image-20250104"], 283 | ), 284 | } 285 | 286 | 287 | @pytest.mark.parametrize("name, scenario", SCENARIOS.items(), ids=list(SCENARIOS)) 288 | @patch("ami_deprecation_tool.api._get_snapshot_ids", return_value=[]) 289 | @patch("ami_deprecation_tool.api.boto3") 290 | def test_deprecation_scenarios(mock_boto, _snap, name: str, scenario: Scenario): 291 | # common boto plumbing 292 | base, r1, r2 = MagicMock(), MagicMock(), MagicMock() 293 | base.describe_regions.return_value = {"Regions": [{"RegionName": "region1"}, {"RegionName": "region2"}]} 294 | r1.describe_images.return_value = scenario.region1 295 | r2.describe_images.return_value = scenario.region2 296 | mock_boto.client.side_effect = [base, r1, r2] 297 | 298 | cfg = configmodels.ConfigModel(images={"image-20250101": scenario.policy}, options={}) 299 | actions = api.deprecate(cfg, True) 300 | 301 | assert actions == expect( 302 | scenario.delete, 303 | scenario.deprecate, 304 | scenario.keep, 305 | scenario.skip, 306 | scenario.policy, 307 | ) 308 | -------------------------------------------------------------------------------- /ami_deprecation_tool/api.py: -------------------------------------------------------------------------------- 1 | import datetime as dt 2 | import logging 3 | from collections import defaultdict 4 | from concurrent.futures import ThreadPoolExecutor 5 | from dataclasses import dataclass, field 6 | from enum import Enum 7 | from itertools import cycle 8 | from typing import Callable 9 | 10 | import boto3 11 | from botocore.exceptions import ClientError 12 | from mypy_boto3_ec2.client import EC2Client 13 | from mypy_boto3_ec2.type_defs import ImageTypeDef 14 | 15 | from .configmodels import ConfigModel, ConfigOptionsModel, ConfigPolicyModel 16 | 17 | logger = logging.getLogger(__name__) 18 | 19 | 20 | @dataclass 21 | class RegionImageContainer: 22 | region: str 23 | image_id: str 24 | creation_date: dt.datetime 25 | snapshots: list[str] 26 | 27 | 28 | class Action(str, Enum): 29 | DELETE = "delete" 30 | DEPRECATE = "deprecate" 31 | 32 | 33 | @dataclass 34 | class ActionImages: 35 | delete: list[str] = field(default_factory=list) 36 | deprecate: list[str] = field(default_factory=list) 37 | keep: list[str] = field(default_factory=list) 38 | skip: list[str] = field(default_factory=list) 39 | 40 | 41 | @dataclass 42 | class Actions: 43 | images: ActionImages 44 | policy: dict[str, str | int] 45 | 46 | 47 | def deprecate(config: ConfigModel, dry_run: bool) -> dict[str, Actions]: 48 | """ 49 | Identify images to be deprecated and apply specified policy 50 | 51 | :param config: the deprecation policy config 52 | :type config: ConfigModel 53 | :param dry_run: disables actioning the images if True 54 | :type dry_run: bool 55 | :return: dictionary mapping action name (e.g. keep, deprecate, delete) to a list of images 56 | :rtype: dict[str, Actions] 57 | """ 58 | client = boto3.client("ec2") 59 | regions = _get_all_regions(client) 60 | region_clients = {} 61 | 62 | if dry_run: 63 | logger.info("DRY_RUN is enabled, all actions will be skipped") 64 | 65 | for region in regions: 66 | region_clients[region] = boto3.client("ec2", region_name=region) 67 | 68 | actions_dict = {} 69 | for image_name, policy in config.images.items(): 70 | # key image_name, and value is a list of tuples containing (region, ami) 71 | region_images = defaultdict(list) 72 | 73 | with ThreadPoolExecutor(max_workers=max(1, int(len(regions) / 2))) as executor: 74 | images = executor.map(_get_images, region_clients.values(), cycle([image_name]), cycle([config.options])) 75 | images_by_region = dict(zip(region_clients.keys(), list(images))) 76 | 77 | for region, images_in_region in images_by_region.items(): 78 | for image in images_in_region: 79 | region_images[image["Name"]].append( 80 | RegionImageContainer( 81 | region, 82 | image["ImageId"], 83 | dt.datetime.fromisoformat(str(image["CreationDate"])), 84 | _get_snapshot_ids(image), 85 | ) 86 | ) 87 | 88 | sorted_image_regions = dict(sorted(region_images.items())) 89 | 90 | image_actions = _apply_deprecation_policy(sorted_image_regions, region_clients, policy, dry_run) 91 | 92 | actions_dict[image_name] = Actions(policy=dict(policy), images=image_actions) 93 | 94 | return actions_dict 95 | 96 | 97 | def _image_is_expired(images: list[RegionImageContainer], policy: ConfigPolicyModel) -> bool: 98 | """ 99 | Identify if image is past expiration based on policy 100 | 101 | :param images: a list of tuples pairing the region name with the ami id in that region 102 | :type images: list[RegionImageContainer] 103 | :param policy: The deprecation policy for the given image set 104 | :type policy: ConfigPolicyModel 105 | :return: boolean representing if the image is past the policy expiration date 106 | :rtype: bool 107 | """ 108 | 109 | # check if the image is has existed longer than keep_days days 110 | cutoff = dt.datetime.now() - dt.timedelta(days=policy.keep_days) 111 | 112 | return images[0].creation_date < cutoff 113 | 114 | 115 | def _apply_deprecation_policy( 116 | region_images: dict[str, list[RegionImageContainer]], 117 | region_clients: dict[str, EC2Client], 118 | policy: ConfigPolicyModel, 119 | dry_run: bool, 120 | ) -> ActionImages: 121 | """ 122 | Identify images to be deprecated based on policy and upload completeness (i.e. an 123 | image is present in all regions) 124 | 125 | :param region_images: a dictionary keyed on image names mapped to a list of tuples pairing the 126 | region name with the ami id in that region 127 | :type region_images: dict[str, list[RegionImageContainer]] 128 | :param region_clients: a dicitonary mapping region names to an EC2Client for that region 129 | :type region_clients: dict[str, EC2Client] 130 | :param policy: The deprecation policy for the given image set 131 | :type policy: ConfigPolicyModel 132 | :param dry_run: disables actioning the images if True 133 | :type dry_run: bool 134 | :return: dictionary mapping action name (e.g. keep, deprecate, delete) to a list of images 135 | :rtype: ActionImages 136 | """ 137 | completed_serials: int = 0 138 | 139 | image_actions = ActionImages() 140 | 141 | for image in sorted(list(region_images.keys()), reverse=True): 142 | if completed_serials == policy.keep and _image_is_expired(region_images[image], policy): 143 | break 144 | 145 | # check if image exists in all regions (i.e. is a completed upload) 146 | is_complete = len(region_images[image]) == len(region_clients.keys()) 147 | if is_complete: 148 | completed_serials += 1 149 | image_actions.keep.append(image) 150 | else: 151 | image_actions.skip.append(image) 152 | region_images.pop(image) 153 | 154 | match policy.action: 155 | case Action.DEPRECATE: 156 | image_actions.deprecate = list(region_images.keys()) 157 | _deprecate_images(dry_run, region_clients, region_images) 158 | case Action.DELETE: 159 | image_actions.delete = list(region_images.keys()) 160 | _delete_images(dry_run, region_clients, region_images) 161 | 162 | return image_actions 163 | 164 | 165 | def _get_all_regions(client: EC2Client) -> list[str]: 166 | """ 167 | Get all regions known to the active AWS profile 168 | 169 | :param client: an active EC2Client 170 | :type: EC2Client 171 | :return: a list of region names 172 | :rtype: list[str] 173 | """ 174 | resp = client.describe_regions() 175 | return [r["RegionName"] for r in resp["Regions"]] 176 | 177 | 178 | def _get_images(client: EC2Client, name: str, options: ConfigOptionsModel) -> list[ImageTypeDef]: 179 | """ 180 | Get images in a single region matching the provided name pattern. 181 | 182 | :param client: an active EC2Client for a region 183 | :type client: EC2Client 184 | :param name: An image name pattern to be searched 185 | :type name: str 186 | :param options: Tool configuration options 187 | :type options: ConfigOptionsModel 188 | :return: the images in reverse order sorted by Name 189 | :rtype: list[ImageTypeDef] 190 | """ 191 | 192 | def _is_deprecated(image: ImageTypeDef) -> bool: 193 | deprecation_time = image.get("DeprecationTime", "") 194 | if not deprecation_time: 195 | return False 196 | if dt.datetime.fromisoformat(deprecation_time.rstrip("Z")) > dt.datetime.now(): 197 | return False 198 | return True 199 | 200 | # assumes images are consistently sortable 201 | images = client.describe_images( 202 | Owners=["self"], 203 | IncludeDisabled=options.include_disabled, 204 | Filters=[{"Name": "name", "Values": [name]}], 205 | ExecutableUsers=options.executable_users, 206 | )["Images"] 207 | # deprecated images are always returned for the owner, so filtering in 208 | # describe images does nothing 209 | if not options.include_deprecated: 210 | images = [image for image in images if not _is_deprecated(image)] 211 | return sorted(images, key=lambda x: x["Name"], reverse=True) 212 | 213 | 214 | def _get_snapshot_ids(image: ImageTypeDef) -> list[str]: 215 | """ 216 | Get list of snapshot ids from provided image 217 | :param image: an ec2 image 218 | :type image: ImageTypeDef 219 | :return: a list of snapshot ids associated with the given image 220 | :rtype: list[str] 221 | """ 222 | return [ 223 | device["Ebs"]["SnapshotId"] 224 | for device in image["BlockDeviceMappings"] 225 | if "Ebs" in device and "SnapshotId" in device["Ebs"] 226 | ] 227 | 228 | 229 | def _concurrent_map_operation( 230 | action_func: Callable, 231 | image_name: str, 232 | region_clients: dict[str, EC2Client], 233 | image_containers: list[RegionImageContainer], 234 | dry_run: bool, 235 | ): 236 | with ThreadPoolExecutor(max_workers=max(1, int(len(region_clients) / 2))) as executor: 237 | executor.map(action_func, cycle([image_name]), cycle([region_clients]), image_containers, cycle([dry_run])) 238 | 239 | 240 | def _deprecate_images( 241 | dry_run: bool, region_clients: dict[str, EC2Client], images: dict[str, list[RegionImageContainer]] 242 | ) -> None: 243 | """ 244 | Mark provided images for deprecation 1 minute in the future. 1 minute is the minimum allowed deprecation time. 245 | 246 | :param dry_run: disables actioning the images if True 247 | :type dry_run: bool 248 | :param region_clients: a dicitonary mapping region names to an EC2Client for that region 249 | :type region_clients: dict[str, EC2Client] 250 | :param images: a dictionary keyed on image names mapped to a list of tuples pairing the 251 | region name with the ami id in that region 252 | :type images: dict[str, list[RegionImageContainer]] 253 | """ 254 | for image_name, image_containers in images.items(): 255 | # Set DeprecationTime 1 minute in the future 256 | logger.info(f"Found image for deprecation ({image_name})") 257 | _concurrent_map_operation(_deprecate_image, image_name, region_clients, image_containers, dry_run) 258 | 259 | 260 | def _deprecate_image(image_name: str, clients: dict[str, EC2Client], image: RegionImageContainer, dry_run: bool): 261 | logger.info(f"Deprecating image ({image_name} , {image.image_id}) in region ({image.region})") 262 | client: EC2Client = clients[image.region] 263 | _perform_operation( 264 | client.enable_image_deprecation, 265 | { 266 | "ImageId": image.image_id, 267 | "DeprecateAt": str(dt.datetime.now() + dt.timedelta(minutes=1)), 268 | "DryRun": dry_run, 269 | }, 270 | ) 271 | 272 | 273 | def _delete_images( 274 | dry_run: bool, region_clients: dict[str, EC2Client], images: dict[str, list[RegionImageContainer]] 275 | ) -> None: 276 | """ 277 | Delete/Deregister provided images 278 | 279 | :param dry_run: disables actioning the images if True 280 | :type dry_run: bool 281 | :param region_clients: a dicitonary mapping region names to an EC2Client for that region 282 | :type region_clients: dict[str, EC2Client] 283 | :param images: a dictionary keyed on image names mapped to a list of tuples pairing the 284 | region name with the ami id in that region 285 | :type images: dict[str, list[RegionImageContainer]] 286 | """ 287 | for image_name, image_containers in images.items(): 288 | logger.info(f"Found image for deletion ({image_name})") 289 | _concurrent_map_operation(_delete_image, image_name, region_clients, image_containers, dry_run) 290 | 291 | 292 | def _delete_image(image_name: str, clients: dict[str, EC2Client], image: RegionImageContainer, dry_run: bool): 293 | logger.info(f"Deleting image ({image_name}, {image.image_id}) in region ({image.region})") 294 | client = clients[image.region] 295 | _perform_operation(client.deregister_image, {"ImageId": image.image_id, "DryRun": dry_run}) 296 | for snapshot_id in image.snapshots: 297 | _delete_snapshot(client, snapshot_id, dry_run) 298 | 299 | 300 | def _delete_snapshot(client: EC2Client, snapshot_id: str, dry_run: bool): 301 | result = client.describe_images( 302 | Filters=[ 303 | { 304 | "Name": "block-device-mapping.snapshot-id", 305 | "Values": [snapshot_id], 306 | } 307 | ] 308 | ) 309 | images_using_snapshot = [i["ImageId"] for i in result.get("Images", [])] 310 | 311 | if images_using_snapshot: 312 | dry_run_addendum = ( 313 | " dry-run will always indicate a skipped snapshot since the image wasn't deleted." if dry_run else "" 314 | ) 315 | joined_images = "\n - ".join(i for i in images_using_snapshot) 316 | logger.info( 317 | f"{len(images_using_snapshot)} images are using snapshot ({snapshot_id}), skipping delete." 318 | f"{dry_run_addendum}" 319 | f"\n - {joined_images}" 320 | ) 321 | else: 322 | logger.info(f"Deleting associated snapshot: {snapshot_id}") 323 | _perform_operation(client.delete_snapshot, {"SnapshotId": snapshot_id, "DryRun": dry_run}) 324 | 325 | 326 | def _perform_operation(operation: Callable, args: dict[str, str | bool]): 327 | """ 328 | A thin wrapper around a callable to handle common exceptions (specifically dry-run) 329 | 330 | :param operation: a function to be called with the given arguments 331 | :type operation: Callable 332 | :param args: the arguments to pass to operation 333 | :type args: dict[str, str | bool] 334 | """ 335 | logger.debug(f"Performing operation {operation} with the following arguments: {args}") 336 | try: 337 | operation(**args) 338 | except ClientError as e: 339 | match e.response["Error"]["Code"]: 340 | case "DryRunOperation": 341 | pass 342 | case _: 343 | raise e 344 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "annotated-types" 5 | version = "0.7.0" 6 | description = "Reusable constraint types to use with typing.Annotated" 7 | optional = false 8 | python-versions = ">=3.8" 9 | files = [ 10 | {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, 11 | {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, 12 | ] 13 | 14 | [[package]] 15 | name = "boto3" 16 | version = "1.40.68" 17 | description = "The AWS SDK for Python" 18 | optional = false 19 | python-versions = ">=3.9" 20 | files = [ 21 | {file = "boto3-1.40.68-py3-none-any.whl", hash = "sha256:4f08115e3a4d1e1056003e433d393e78c20da6af7753409992bb33fb69f04186"}, 22 | {file = "boto3-1.40.68.tar.gz", hash = "sha256:c7994989e5bbba071b7c742adfba35773cf03e87f5d3f9f2b0a18c1664417b61"}, 23 | ] 24 | 25 | [package.dependencies] 26 | botocore = ">=1.40.68,<1.41.0" 27 | jmespath = ">=0.7.1,<2.0.0" 28 | s3transfer = ">=0.14.0,<0.15.0" 29 | 30 | [package.extras] 31 | crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] 32 | 33 | [[package]] 34 | name = "boto3-stubs" 35 | version = "1.40.64" 36 | description = "Type annotations for boto3 1.40.64 generated with mypy-boto3-builder 8.11.0" 37 | optional = false 38 | python-versions = ">=3.8" 39 | files = [ 40 | {file = "boto3_stubs-1.40.64-py3-none-any.whl", hash = "sha256:c0c0dd70c02b08550ca596e8799586b3669e9352844db22401e76556b05d71a3"}, 41 | {file = "boto3_stubs-1.40.64.tar.gz", hash = "sha256:af4a7fb67bda0c1277afcd45a25065605567d260a8afd34bbdf1799d2492334f"}, 42 | ] 43 | 44 | [package.dependencies] 45 | botocore-stubs = "*" 46 | mypy-boto3-cloudformation = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"essential\""} 47 | mypy-boto3-dynamodb = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"essential\""} 48 | mypy-boto3-ec2 = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"essential\""} 49 | mypy-boto3-lambda = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"essential\""} 50 | mypy-boto3-rds = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"essential\""} 51 | mypy-boto3-s3 = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"essential\""} 52 | mypy-boto3-sqs = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"essential\""} 53 | types-s3transfer = "*" 54 | typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.12\""} 55 | 56 | [package.extras] 57 | accessanalyzer = ["mypy-boto3-accessanalyzer (>=1.40.0,<1.41.0)"] 58 | account = ["mypy-boto3-account (>=1.40.0,<1.41.0)"] 59 | acm = ["mypy-boto3-acm (>=1.40.0,<1.41.0)"] 60 | acm-pca = ["mypy-boto3-acm-pca (>=1.40.0,<1.41.0)"] 61 | aiops = ["mypy-boto3-aiops (>=1.40.0,<1.41.0)"] 62 | all = ["mypy-boto3-accessanalyzer (>=1.40.0,<1.41.0)", "mypy-boto3-account (>=1.40.0,<1.41.0)", "mypy-boto3-acm (>=1.40.0,<1.41.0)", "mypy-boto3-acm-pca (>=1.40.0,<1.41.0)", "mypy-boto3-aiops (>=1.40.0,<1.41.0)", "mypy-boto3-amp (>=1.40.0,<1.41.0)", "mypy-boto3-amplify (>=1.40.0,<1.41.0)", "mypy-boto3-amplifybackend (>=1.40.0,<1.41.0)", "mypy-boto3-amplifyuibuilder (>=1.40.0,<1.41.0)", "mypy-boto3-apigateway (>=1.40.0,<1.41.0)", "mypy-boto3-apigatewaymanagementapi (>=1.40.0,<1.41.0)", "mypy-boto3-apigatewayv2 (>=1.40.0,<1.41.0)", "mypy-boto3-appconfig (>=1.40.0,<1.41.0)", "mypy-boto3-appconfigdata (>=1.40.0,<1.41.0)", "mypy-boto3-appfabric (>=1.40.0,<1.41.0)", "mypy-boto3-appflow (>=1.40.0,<1.41.0)", "mypy-boto3-appintegrations (>=1.40.0,<1.41.0)", "mypy-boto3-application-autoscaling (>=1.40.0,<1.41.0)", "mypy-boto3-application-insights (>=1.40.0,<1.41.0)", "mypy-boto3-application-signals (>=1.40.0,<1.41.0)", "mypy-boto3-applicationcostprofiler (>=1.40.0,<1.41.0)", "mypy-boto3-appmesh (>=1.40.0,<1.41.0)", "mypy-boto3-apprunner (>=1.40.0,<1.41.0)", "mypy-boto3-appstream (>=1.40.0,<1.41.0)", "mypy-boto3-appsync (>=1.40.0,<1.41.0)", "mypy-boto3-arc-region-switch (>=1.40.0,<1.41.0)", "mypy-boto3-arc-zonal-shift (>=1.40.0,<1.41.0)", "mypy-boto3-artifact (>=1.40.0,<1.41.0)", "mypy-boto3-athena (>=1.40.0,<1.41.0)", "mypy-boto3-auditmanager (>=1.40.0,<1.41.0)", "mypy-boto3-autoscaling (>=1.40.0,<1.41.0)", "mypy-boto3-autoscaling-plans (>=1.40.0,<1.41.0)", "mypy-boto3-b2bi (>=1.40.0,<1.41.0)", "mypy-boto3-backup (>=1.40.0,<1.41.0)", "mypy-boto3-backup-gateway (>=1.40.0,<1.41.0)", "mypy-boto3-backupsearch (>=1.40.0,<1.41.0)", "mypy-boto3-batch (>=1.40.0,<1.41.0)", "mypy-boto3-bcm-dashboards (>=1.40.0,<1.41.0)", "mypy-boto3-bcm-data-exports (>=1.40.0,<1.41.0)", "mypy-boto3-bcm-pricing-calculator (>=1.40.0,<1.41.0)", "mypy-boto3-bcm-recommended-actions (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-agent (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-agent-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-agentcore (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-agentcore-control (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-data-automation (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-data-automation-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-billing (>=1.40.0,<1.41.0)", "mypy-boto3-billingconductor (>=1.40.0,<1.41.0)", "mypy-boto3-braket (>=1.40.0,<1.41.0)", "mypy-boto3-budgets (>=1.40.0,<1.41.0)", "mypy-boto3-ce (>=1.40.0,<1.41.0)", "mypy-boto3-chatbot (>=1.40.0,<1.41.0)", "mypy-boto3-chime (>=1.40.0,<1.41.0)", "mypy-boto3-chime-sdk-identity (>=1.40.0,<1.41.0)", "mypy-boto3-chime-sdk-media-pipelines (>=1.40.0,<1.41.0)", "mypy-boto3-chime-sdk-meetings (>=1.40.0,<1.41.0)", "mypy-boto3-chime-sdk-messaging (>=1.40.0,<1.41.0)", "mypy-boto3-chime-sdk-voice (>=1.40.0,<1.41.0)", "mypy-boto3-cleanrooms (>=1.40.0,<1.41.0)", "mypy-boto3-cleanroomsml (>=1.40.0,<1.41.0)", "mypy-boto3-cloud9 (>=1.40.0,<1.41.0)", "mypy-boto3-cloudcontrol (>=1.40.0,<1.41.0)", "mypy-boto3-clouddirectory (>=1.40.0,<1.41.0)", "mypy-boto3-cloudformation (>=1.40.0,<1.41.0)", "mypy-boto3-cloudfront (>=1.40.0,<1.41.0)", "mypy-boto3-cloudfront-keyvaluestore (>=1.40.0,<1.41.0)", "mypy-boto3-cloudhsm (>=1.40.0,<1.41.0)", "mypy-boto3-cloudhsmv2 (>=1.40.0,<1.41.0)", "mypy-boto3-cloudsearch (>=1.40.0,<1.41.0)", "mypy-boto3-cloudsearchdomain (>=1.40.0,<1.41.0)", "mypy-boto3-cloudtrail (>=1.40.0,<1.41.0)", "mypy-boto3-cloudtrail-data (>=1.40.0,<1.41.0)", "mypy-boto3-cloudwatch (>=1.40.0,<1.41.0)", "mypy-boto3-codeartifact (>=1.40.0,<1.41.0)", "mypy-boto3-codebuild (>=1.40.0,<1.41.0)", "mypy-boto3-codecatalyst (>=1.40.0,<1.41.0)", "mypy-boto3-codecommit (>=1.40.0,<1.41.0)", "mypy-boto3-codeconnections (>=1.40.0,<1.41.0)", "mypy-boto3-codedeploy (>=1.40.0,<1.41.0)", "mypy-boto3-codeguru-reviewer (>=1.40.0,<1.41.0)", "mypy-boto3-codeguru-security (>=1.40.0,<1.41.0)", "mypy-boto3-codeguruprofiler (>=1.40.0,<1.41.0)", "mypy-boto3-codepipeline (>=1.40.0,<1.41.0)", "mypy-boto3-codestar-connections (>=1.40.0,<1.41.0)", "mypy-boto3-codestar-notifications (>=1.40.0,<1.41.0)", "mypy-boto3-cognito-identity (>=1.40.0,<1.41.0)", "mypy-boto3-cognito-idp (>=1.40.0,<1.41.0)", "mypy-boto3-cognito-sync (>=1.40.0,<1.41.0)", "mypy-boto3-comprehend (>=1.40.0,<1.41.0)", "mypy-boto3-comprehendmedical (>=1.40.0,<1.41.0)", "mypy-boto3-compute-optimizer (>=1.40.0,<1.41.0)", "mypy-boto3-config (>=1.40.0,<1.41.0)", "mypy-boto3-connect (>=1.40.0,<1.41.0)", "mypy-boto3-connect-contact-lens (>=1.40.0,<1.41.0)", "mypy-boto3-connectcampaigns (>=1.40.0,<1.41.0)", "mypy-boto3-connectcampaignsv2 (>=1.40.0,<1.41.0)", "mypy-boto3-connectcases (>=1.40.0,<1.41.0)", "mypy-boto3-connectparticipant (>=1.40.0,<1.41.0)", "mypy-boto3-controlcatalog (>=1.40.0,<1.41.0)", "mypy-boto3-controltower (>=1.40.0,<1.41.0)", "mypy-boto3-cost-optimization-hub (>=1.40.0,<1.41.0)", "mypy-boto3-cur (>=1.40.0,<1.41.0)", "mypy-boto3-customer-profiles (>=1.40.0,<1.41.0)", "mypy-boto3-databrew (>=1.40.0,<1.41.0)", "mypy-boto3-dataexchange (>=1.40.0,<1.41.0)", "mypy-boto3-datapipeline (>=1.40.0,<1.41.0)", "mypy-boto3-datasync (>=1.40.0,<1.41.0)", "mypy-boto3-datazone (>=1.40.0,<1.41.0)", "mypy-boto3-dax (>=1.40.0,<1.41.0)", "mypy-boto3-deadline (>=1.40.0,<1.41.0)", "mypy-boto3-detective (>=1.40.0,<1.41.0)", "mypy-boto3-devicefarm (>=1.40.0,<1.41.0)", "mypy-boto3-devops-guru (>=1.40.0,<1.41.0)", "mypy-boto3-directconnect (>=1.40.0,<1.41.0)", "mypy-boto3-discovery (>=1.40.0,<1.41.0)", "mypy-boto3-dlm (>=1.40.0,<1.41.0)", "mypy-boto3-dms (>=1.40.0,<1.41.0)", "mypy-boto3-docdb (>=1.40.0,<1.41.0)", "mypy-boto3-docdb-elastic (>=1.40.0,<1.41.0)", "mypy-boto3-drs (>=1.40.0,<1.41.0)", "mypy-boto3-ds (>=1.40.0,<1.41.0)", "mypy-boto3-ds-data (>=1.40.0,<1.41.0)", "mypy-boto3-dsql (>=1.40.0,<1.41.0)", "mypy-boto3-dynamodb (>=1.40.0,<1.41.0)", "mypy-boto3-dynamodbstreams (>=1.40.0,<1.41.0)", "mypy-boto3-ebs (>=1.40.0,<1.41.0)", "mypy-boto3-ec2 (>=1.40.0,<1.41.0)", "mypy-boto3-ec2-instance-connect (>=1.40.0,<1.41.0)", "mypy-boto3-ecr (>=1.40.0,<1.41.0)", "mypy-boto3-ecr-public (>=1.40.0,<1.41.0)", "mypy-boto3-ecs (>=1.40.0,<1.41.0)", "mypy-boto3-efs (>=1.40.0,<1.41.0)", "mypy-boto3-eks (>=1.40.0,<1.41.0)", "mypy-boto3-eks-auth (>=1.40.0,<1.41.0)", "mypy-boto3-elasticache (>=1.40.0,<1.41.0)", "mypy-boto3-elasticbeanstalk (>=1.40.0,<1.41.0)", "mypy-boto3-elastictranscoder (>=1.40.0,<1.41.0)", "mypy-boto3-elb (>=1.40.0,<1.41.0)", "mypy-boto3-elbv2 (>=1.40.0,<1.41.0)", "mypy-boto3-emr (>=1.40.0,<1.41.0)", "mypy-boto3-emr-containers (>=1.40.0,<1.41.0)", "mypy-boto3-emr-serverless (>=1.40.0,<1.41.0)", "mypy-boto3-entityresolution (>=1.40.0,<1.41.0)", "mypy-boto3-es (>=1.40.0,<1.41.0)", "mypy-boto3-events (>=1.40.0,<1.41.0)", "mypy-boto3-evidently (>=1.40.0,<1.41.0)", "mypy-boto3-evs (>=1.40.0,<1.41.0)", "mypy-boto3-finspace (>=1.40.0,<1.41.0)", "mypy-boto3-finspace-data (>=1.40.0,<1.41.0)", "mypy-boto3-firehose (>=1.40.0,<1.41.0)", "mypy-boto3-fis (>=1.40.0,<1.41.0)", "mypy-boto3-fms (>=1.40.0,<1.41.0)", "mypy-boto3-forecast (>=1.40.0,<1.41.0)", "mypy-boto3-forecastquery (>=1.40.0,<1.41.0)", "mypy-boto3-frauddetector (>=1.40.0,<1.41.0)", "mypy-boto3-freetier (>=1.40.0,<1.41.0)", "mypy-boto3-fsx (>=1.40.0,<1.41.0)", "mypy-boto3-gamelift (>=1.40.0,<1.41.0)", "mypy-boto3-gameliftstreams (>=1.40.0,<1.41.0)", "mypy-boto3-geo-maps (>=1.40.0,<1.41.0)", "mypy-boto3-geo-places (>=1.40.0,<1.41.0)", "mypy-boto3-geo-routes (>=1.40.0,<1.41.0)", "mypy-boto3-glacier (>=1.40.0,<1.41.0)", "mypy-boto3-globalaccelerator (>=1.40.0,<1.41.0)", "mypy-boto3-glue (>=1.40.0,<1.41.0)", "mypy-boto3-grafana (>=1.40.0,<1.41.0)", "mypy-boto3-greengrass (>=1.40.0,<1.41.0)", "mypy-boto3-greengrassv2 (>=1.40.0,<1.41.0)", "mypy-boto3-groundstation (>=1.40.0,<1.41.0)", "mypy-boto3-guardduty (>=1.40.0,<1.41.0)", "mypy-boto3-health (>=1.40.0,<1.41.0)", "mypy-boto3-healthlake (>=1.40.0,<1.41.0)", "mypy-boto3-iam (>=1.40.0,<1.41.0)", "mypy-boto3-identitystore (>=1.40.0,<1.41.0)", "mypy-boto3-imagebuilder (>=1.40.0,<1.41.0)", "mypy-boto3-importexport (>=1.40.0,<1.41.0)", "mypy-boto3-inspector (>=1.40.0,<1.41.0)", "mypy-boto3-inspector-scan (>=1.40.0,<1.41.0)", "mypy-boto3-inspector2 (>=1.40.0,<1.41.0)", "mypy-boto3-internetmonitor (>=1.40.0,<1.41.0)", "mypy-boto3-invoicing (>=1.40.0,<1.41.0)", "mypy-boto3-iot (>=1.40.0,<1.41.0)", "mypy-boto3-iot-data (>=1.40.0,<1.41.0)", "mypy-boto3-iot-jobs-data (>=1.40.0,<1.41.0)", "mypy-boto3-iot-managed-integrations (>=1.40.0,<1.41.0)", "mypy-boto3-iotanalytics (>=1.40.0,<1.41.0)", "mypy-boto3-iotdeviceadvisor (>=1.40.0,<1.41.0)", "mypy-boto3-iotevents (>=1.40.0,<1.41.0)", "mypy-boto3-iotevents-data (>=1.40.0,<1.41.0)", "mypy-boto3-iotfleetwise (>=1.40.0,<1.41.0)", "mypy-boto3-iotsecuretunneling (>=1.40.0,<1.41.0)", "mypy-boto3-iotsitewise (>=1.40.0,<1.41.0)", "mypy-boto3-iotthingsgraph (>=1.40.0,<1.41.0)", "mypy-boto3-iottwinmaker (>=1.40.0,<1.41.0)", "mypy-boto3-iotwireless (>=1.40.0,<1.41.0)", "mypy-boto3-ivs (>=1.40.0,<1.41.0)", "mypy-boto3-ivs-realtime (>=1.40.0,<1.41.0)", "mypy-boto3-ivschat (>=1.40.0,<1.41.0)", "mypy-boto3-kafka (>=1.40.0,<1.41.0)", "mypy-boto3-kafkaconnect (>=1.40.0,<1.41.0)", "mypy-boto3-kendra (>=1.40.0,<1.41.0)", "mypy-boto3-kendra-ranking (>=1.40.0,<1.41.0)", "mypy-boto3-keyspaces (>=1.40.0,<1.41.0)", "mypy-boto3-keyspacesstreams (>=1.40.0,<1.41.0)", "mypy-boto3-kinesis (>=1.40.0,<1.41.0)", "mypy-boto3-kinesis-video-archived-media (>=1.40.0,<1.41.0)", "mypy-boto3-kinesis-video-media (>=1.40.0,<1.41.0)", "mypy-boto3-kinesis-video-signaling (>=1.40.0,<1.41.0)", "mypy-boto3-kinesis-video-webrtc-storage (>=1.40.0,<1.41.0)", "mypy-boto3-kinesisanalytics (>=1.40.0,<1.41.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.40.0,<1.41.0)", "mypy-boto3-kinesisvideo (>=1.40.0,<1.41.0)", "mypy-boto3-kms (>=1.40.0,<1.41.0)", "mypy-boto3-lakeformation (>=1.40.0,<1.41.0)", "mypy-boto3-lambda (>=1.40.0,<1.41.0)", "mypy-boto3-launch-wizard (>=1.40.0,<1.41.0)", "mypy-boto3-lex-models (>=1.40.0,<1.41.0)", "mypy-boto3-lex-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-lexv2-models (>=1.40.0,<1.41.0)", "mypy-boto3-lexv2-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-license-manager (>=1.40.0,<1.41.0)", "mypy-boto3-license-manager-linux-subscriptions (>=1.40.0,<1.41.0)", "mypy-boto3-license-manager-user-subscriptions (>=1.40.0,<1.41.0)", "mypy-boto3-lightsail (>=1.40.0,<1.41.0)", "mypy-boto3-location (>=1.40.0,<1.41.0)", "mypy-boto3-logs (>=1.40.0,<1.41.0)", "mypy-boto3-lookoutequipment (>=1.40.0,<1.41.0)", "mypy-boto3-m2 (>=1.40.0,<1.41.0)", "mypy-boto3-machinelearning (>=1.40.0,<1.41.0)", "mypy-boto3-macie2 (>=1.40.0,<1.41.0)", "mypy-boto3-mailmanager (>=1.40.0,<1.41.0)", "mypy-boto3-managedblockchain (>=1.40.0,<1.41.0)", "mypy-boto3-managedblockchain-query (>=1.40.0,<1.41.0)", "mypy-boto3-marketplace-agreement (>=1.40.0,<1.41.0)", "mypy-boto3-marketplace-catalog (>=1.40.0,<1.41.0)", "mypy-boto3-marketplace-deployment (>=1.40.0,<1.41.0)", "mypy-boto3-marketplace-entitlement (>=1.40.0,<1.41.0)", "mypy-boto3-marketplace-reporting (>=1.40.0,<1.41.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.40.0,<1.41.0)", "mypy-boto3-mediaconnect (>=1.40.0,<1.41.0)", "mypy-boto3-mediaconvert (>=1.40.0,<1.41.0)", "mypy-boto3-medialive (>=1.40.0,<1.41.0)", "mypy-boto3-mediapackage (>=1.40.0,<1.41.0)", "mypy-boto3-mediapackage-vod (>=1.40.0,<1.41.0)", "mypy-boto3-mediapackagev2 (>=1.40.0,<1.41.0)", "mypy-boto3-mediastore (>=1.40.0,<1.41.0)", "mypy-boto3-mediastore-data (>=1.40.0,<1.41.0)", "mypy-boto3-mediatailor (>=1.40.0,<1.41.0)", "mypy-boto3-medical-imaging (>=1.40.0,<1.41.0)", "mypy-boto3-memorydb (>=1.40.0,<1.41.0)", "mypy-boto3-meteringmarketplace (>=1.40.0,<1.41.0)", "mypy-boto3-mgh (>=1.40.0,<1.41.0)", "mypy-boto3-mgn (>=1.40.0,<1.41.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.40.0,<1.41.0)", "mypy-boto3-migrationhub-config (>=1.40.0,<1.41.0)", "mypy-boto3-migrationhuborchestrator (>=1.40.0,<1.41.0)", "mypy-boto3-migrationhubstrategy (>=1.40.0,<1.41.0)", "mypy-boto3-mpa (>=1.40.0,<1.41.0)", "mypy-boto3-mq (>=1.40.0,<1.41.0)", "mypy-boto3-mturk (>=1.40.0,<1.41.0)", "mypy-boto3-mwaa (>=1.40.0,<1.41.0)", "mypy-boto3-neptune (>=1.40.0,<1.41.0)", "mypy-boto3-neptune-graph (>=1.40.0,<1.41.0)", "mypy-boto3-neptunedata (>=1.40.0,<1.41.0)", "mypy-boto3-network-firewall (>=1.40.0,<1.41.0)", "mypy-boto3-networkflowmonitor (>=1.40.0,<1.41.0)", "mypy-boto3-networkmanager (>=1.40.0,<1.41.0)", "mypy-boto3-networkmonitor (>=1.40.0,<1.41.0)", "mypy-boto3-notifications (>=1.40.0,<1.41.0)", "mypy-boto3-notificationscontacts (>=1.40.0,<1.41.0)", "mypy-boto3-oam (>=1.40.0,<1.41.0)", "mypy-boto3-observabilityadmin (>=1.40.0,<1.41.0)", "mypy-boto3-odb (>=1.40.0,<1.41.0)", "mypy-boto3-omics (>=1.40.0,<1.41.0)", "mypy-boto3-opensearch (>=1.40.0,<1.41.0)", "mypy-boto3-opensearchserverless (>=1.40.0,<1.41.0)", "mypy-boto3-organizations (>=1.40.0,<1.41.0)", "mypy-boto3-osis (>=1.40.0,<1.41.0)", "mypy-boto3-outposts (>=1.40.0,<1.41.0)", "mypy-boto3-panorama (>=1.40.0,<1.41.0)", "mypy-boto3-partnercentral-selling (>=1.40.0,<1.41.0)", "mypy-boto3-payment-cryptography (>=1.40.0,<1.41.0)", "mypy-boto3-payment-cryptography-data (>=1.40.0,<1.41.0)", "mypy-boto3-pca-connector-ad (>=1.40.0,<1.41.0)", "mypy-boto3-pca-connector-scep (>=1.40.0,<1.41.0)", "mypy-boto3-pcs (>=1.40.0,<1.41.0)", "mypy-boto3-personalize (>=1.40.0,<1.41.0)", "mypy-boto3-personalize-events (>=1.40.0,<1.41.0)", "mypy-boto3-personalize-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-pi (>=1.40.0,<1.41.0)", "mypy-boto3-pinpoint (>=1.40.0,<1.41.0)", "mypy-boto3-pinpoint-email (>=1.40.0,<1.41.0)", "mypy-boto3-pinpoint-sms-voice (>=1.40.0,<1.41.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.40.0,<1.41.0)", "mypy-boto3-pipes (>=1.40.0,<1.41.0)", "mypy-boto3-polly (>=1.40.0,<1.41.0)", "mypy-boto3-pricing (>=1.40.0,<1.41.0)", "mypy-boto3-proton (>=1.40.0,<1.41.0)", "mypy-boto3-qapps (>=1.40.0,<1.41.0)", "mypy-boto3-qbusiness (>=1.40.0,<1.41.0)", "mypy-boto3-qconnect (>=1.40.0,<1.41.0)", "mypy-boto3-quicksight (>=1.40.0,<1.41.0)", "mypy-boto3-ram (>=1.40.0,<1.41.0)", "mypy-boto3-rbin (>=1.40.0,<1.41.0)", "mypy-boto3-rds (>=1.40.0,<1.41.0)", "mypy-boto3-rds-data (>=1.40.0,<1.41.0)", "mypy-boto3-redshift (>=1.40.0,<1.41.0)", "mypy-boto3-redshift-data (>=1.40.0,<1.41.0)", "mypy-boto3-redshift-serverless (>=1.40.0,<1.41.0)", "mypy-boto3-rekognition (>=1.40.0,<1.41.0)", "mypy-boto3-repostspace (>=1.40.0,<1.41.0)", "mypy-boto3-resiliencehub (>=1.40.0,<1.41.0)", "mypy-boto3-resource-explorer-2 (>=1.40.0,<1.41.0)", "mypy-boto3-resource-groups (>=1.40.0,<1.41.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.40.0,<1.41.0)", "mypy-boto3-rolesanywhere (>=1.40.0,<1.41.0)", "mypy-boto3-route53 (>=1.40.0,<1.41.0)", "mypy-boto3-route53-recovery-cluster (>=1.40.0,<1.41.0)", "mypy-boto3-route53-recovery-control-config (>=1.40.0,<1.41.0)", "mypy-boto3-route53-recovery-readiness (>=1.40.0,<1.41.0)", "mypy-boto3-route53domains (>=1.40.0,<1.41.0)", "mypy-boto3-route53profiles (>=1.40.0,<1.41.0)", "mypy-boto3-route53resolver (>=1.40.0,<1.41.0)", "mypy-boto3-rtbfabric (>=1.40.0,<1.41.0)", "mypy-boto3-rum (>=1.40.0,<1.41.0)", "mypy-boto3-s3 (>=1.40.0,<1.41.0)", "mypy-boto3-s3control (>=1.40.0,<1.41.0)", "mypy-boto3-s3outposts (>=1.40.0,<1.41.0)", "mypy-boto3-s3tables (>=1.40.0,<1.41.0)", "mypy-boto3-s3vectors (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-edge (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-geospatial (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-metrics (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-savingsplans (>=1.40.0,<1.41.0)", "mypy-boto3-scheduler (>=1.40.0,<1.41.0)", "mypy-boto3-schemas (>=1.40.0,<1.41.0)", "mypy-boto3-sdb (>=1.40.0,<1.41.0)", "mypy-boto3-secretsmanager (>=1.40.0,<1.41.0)", "mypy-boto3-security-ir (>=1.40.0,<1.41.0)", "mypy-boto3-securityhub (>=1.40.0,<1.41.0)", "mypy-boto3-securitylake (>=1.40.0,<1.41.0)", "mypy-boto3-serverlessrepo (>=1.40.0,<1.41.0)", "mypy-boto3-service-quotas (>=1.40.0,<1.41.0)", "mypy-boto3-servicecatalog (>=1.40.0,<1.41.0)", "mypy-boto3-servicecatalog-appregistry (>=1.40.0,<1.41.0)", "mypy-boto3-servicediscovery (>=1.40.0,<1.41.0)", "mypy-boto3-ses (>=1.40.0,<1.41.0)", "mypy-boto3-sesv2 (>=1.40.0,<1.41.0)", "mypy-boto3-shield (>=1.40.0,<1.41.0)", "mypy-boto3-signer (>=1.40.0,<1.41.0)", "mypy-boto3-simspaceweaver (>=1.40.0,<1.41.0)", "mypy-boto3-snow-device-management (>=1.40.0,<1.41.0)", "mypy-boto3-snowball (>=1.40.0,<1.41.0)", "mypy-boto3-sns (>=1.40.0,<1.41.0)", "mypy-boto3-socialmessaging (>=1.40.0,<1.41.0)", "mypy-boto3-sqs (>=1.40.0,<1.41.0)", "mypy-boto3-ssm (>=1.40.0,<1.41.0)", "mypy-boto3-ssm-contacts (>=1.40.0,<1.41.0)", "mypy-boto3-ssm-guiconnect (>=1.40.0,<1.41.0)", "mypy-boto3-ssm-incidents (>=1.40.0,<1.41.0)", "mypy-boto3-ssm-quicksetup (>=1.40.0,<1.41.0)", "mypy-boto3-ssm-sap (>=1.40.0,<1.41.0)", "mypy-boto3-sso (>=1.40.0,<1.41.0)", "mypy-boto3-sso-admin (>=1.40.0,<1.41.0)", "mypy-boto3-sso-oidc (>=1.40.0,<1.41.0)", "mypy-boto3-stepfunctions (>=1.40.0,<1.41.0)", "mypy-boto3-storagegateway (>=1.40.0,<1.41.0)", "mypy-boto3-sts (>=1.40.0,<1.41.0)", "mypy-boto3-supplychain (>=1.40.0,<1.41.0)", "mypy-boto3-support (>=1.40.0,<1.41.0)", "mypy-boto3-support-app (>=1.40.0,<1.41.0)", "mypy-boto3-swf (>=1.40.0,<1.41.0)", "mypy-boto3-synthetics (>=1.40.0,<1.41.0)", "mypy-boto3-taxsettings (>=1.40.0,<1.41.0)", "mypy-boto3-textract (>=1.40.0,<1.41.0)", "mypy-boto3-timestream-influxdb (>=1.40.0,<1.41.0)", "mypy-boto3-timestream-query (>=1.40.0,<1.41.0)", "mypy-boto3-timestream-write (>=1.40.0,<1.41.0)", "mypy-boto3-tnb (>=1.40.0,<1.41.0)", "mypy-boto3-transcribe (>=1.40.0,<1.41.0)", "mypy-boto3-transfer (>=1.40.0,<1.41.0)", "mypy-boto3-translate (>=1.40.0,<1.41.0)", "mypy-boto3-trustedadvisor (>=1.40.0,<1.41.0)", "mypy-boto3-verifiedpermissions (>=1.40.0,<1.41.0)", "mypy-boto3-voice-id (>=1.40.0,<1.41.0)", "mypy-boto3-vpc-lattice (>=1.40.0,<1.41.0)", "mypy-boto3-waf (>=1.40.0,<1.41.0)", "mypy-boto3-waf-regional (>=1.40.0,<1.41.0)", "mypy-boto3-wafv2 (>=1.40.0,<1.41.0)", "mypy-boto3-wellarchitected (>=1.40.0,<1.41.0)", "mypy-boto3-wisdom (>=1.40.0,<1.41.0)", "mypy-boto3-workdocs (>=1.40.0,<1.41.0)", "mypy-boto3-workmail (>=1.40.0,<1.41.0)", "mypy-boto3-workmailmessageflow (>=1.40.0,<1.41.0)", "mypy-boto3-workspaces (>=1.40.0,<1.41.0)", "mypy-boto3-workspaces-instances (>=1.40.0,<1.41.0)", "mypy-boto3-workspaces-thin-client (>=1.40.0,<1.41.0)", "mypy-boto3-workspaces-web (>=1.40.0,<1.41.0)", "mypy-boto3-xray (>=1.40.0,<1.41.0)"] 63 | amp = ["mypy-boto3-amp (>=1.40.0,<1.41.0)"] 64 | amplify = ["mypy-boto3-amplify (>=1.40.0,<1.41.0)"] 65 | amplifybackend = ["mypy-boto3-amplifybackend (>=1.40.0,<1.41.0)"] 66 | amplifyuibuilder = ["mypy-boto3-amplifyuibuilder (>=1.40.0,<1.41.0)"] 67 | apigateway = ["mypy-boto3-apigateway (>=1.40.0,<1.41.0)"] 68 | apigatewaymanagementapi = ["mypy-boto3-apigatewaymanagementapi (>=1.40.0,<1.41.0)"] 69 | apigatewayv2 = ["mypy-boto3-apigatewayv2 (>=1.40.0,<1.41.0)"] 70 | appconfig = ["mypy-boto3-appconfig (>=1.40.0,<1.41.0)"] 71 | appconfigdata = ["mypy-boto3-appconfigdata (>=1.40.0,<1.41.0)"] 72 | appfabric = ["mypy-boto3-appfabric (>=1.40.0,<1.41.0)"] 73 | appflow = ["mypy-boto3-appflow (>=1.40.0,<1.41.0)"] 74 | appintegrations = ["mypy-boto3-appintegrations (>=1.40.0,<1.41.0)"] 75 | application-autoscaling = ["mypy-boto3-application-autoscaling (>=1.40.0,<1.41.0)"] 76 | application-insights = ["mypy-boto3-application-insights (>=1.40.0,<1.41.0)"] 77 | application-signals = ["mypy-boto3-application-signals (>=1.40.0,<1.41.0)"] 78 | applicationcostprofiler = ["mypy-boto3-applicationcostprofiler (>=1.40.0,<1.41.0)"] 79 | appmesh = ["mypy-boto3-appmesh (>=1.40.0,<1.41.0)"] 80 | apprunner = ["mypy-boto3-apprunner (>=1.40.0,<1.41.0)"] 81 | appstream = ["mypy-boto3-appstream (>=1.40.0,<1.41.0)"] 82 | appsync = ["mypy-boto3-appsync (>=1.40.0,<1.41.0)"] 83 | arc-region-switch = ["mypy-boto3-arc-region-switch (>=1.40.0,<1.41.0)"] 84 | arc-zonal-shift = ["mypy-boto3-arc-zonal-shift (>=1.40.0,<1.41.0)"] 85 | artifact = ["mypy-boto3-artifact (>=1.40.0,<1.41.0)"] 86 | athena = ["mypy-boto3-athena (>=1.40.0,<1.41.0)"] 87 | auditmanager = ["mypy-boto3-auditmanager (>=1.40.0,<1.41.0)"] 88 | autoscaling = ["mypy-boto3-autoscaling (>=1.40.0,<1.41.0)"] 89 | autoscaling-plans = ["mypy-boto3-autoscaling-plans (>=1.40.0,<1.41.0)"] 90 | b2bi = ["mypy-boto3-b2bi (>=1.40.0,<1.41.0)"] 91 | backup = ["mypy-boto3-backup (>=1.40.0,<1.41.0)"] 92 | backup-gateway = ["mypy-boto3-backup-gateway (>=1.40.0,<1.41.0)"] 93 | backupsearch = ["mypy-boto3-backupsearch (>=1.40.0,<1.41.0)"] 94 | batch = ["mypy-boto3-batch (>=1.40.0,<1.41.0)"] 95 | bcm-dashboards = ["mypy-boto3-bcm-dashboards (>=1.40.0,<1.41.0)"] 96 | bcm-data-exports = ["mypy-boto3-bcm-data-exports (>=1.40.0,<1.41.0)"] 97 | bcm-pricing-calculator = ["mypy-boto3-bcm-pricing-calculator (>=1.40.0,<1.41.0)"] 98 | bcm-recommended-actions = ["mypy-boto3-bcm-recommended-actions (>=1.40.0,<1.41.0)"] 99 | bedrock = ["mypy-boto3-bedrock (>=1.40.0,<1.41.0)"] 100 | bedrock-agent = ["mypy-boto3-bedrock-agent (>=1.40.0,<1.41.0)"] 101 | bedrock-agent-runtime = ["mypy-boto3-bedrock-agent-runtime (>=1.40.0,<1.41.0)"] 102 | bedrock-agentcore = ["mypy-boto3-bedrock-agentcore (>=1.40.0,<1.41.0)"] 103 | bedrock-agentcore-control = ["mypy-boto3-bedrock-agentcore-control (>=1.40.0,<1.41.0)"] 104 | bedrock-data-automation = ["mypy-boto3-bedrock-data-automation (>=1.40.0,<1.41.0)"] 105 | bedrock-data-automation-runtime = ["mypy-boto3-bedrock-data-automation-runtime (>=1.40.0,<1.41.0)"] 106 | bedrock-runtime = ["mypy-boto3-bedrock-runtime (>=1.40.0,<1.41.0)"] 107 | billing = ["mypy-boto3-billing (>=1.40.0,<1.41.0)"] 108 | billingconductor = ["mypy-boto3-billingconductor (>=1.40.0,<1.41.0)"] 109 | boto3 = ["boto3 (==1.40.64)"] 110 | braket = ["mypy-boto3-braket (>=1.40.0,<1.41.0)"] 111 | budgets = ["mypy-boto3-budgets (>=1.40.0,<1.41.0)"] 112 | ce = ["mypy-boto3-ce (>=1.40.0,<1.41.0)"] 113 | chatbot = ["mypy-boto3-chatbot (>=1.40.0,<1.41.0)"] 114 | chime = ["mypy-boto3-chime (>=1.40.0,<1.41.0)"] 115 | chime-sdk-identity = ["mypy-boto3-chime-sdk-identity (>=1.40.0,<1.41.0)"] 116 | chime-sdk-media-pipelines = ["mypy-boto3-chime-sdk-media-pipelines (>=1.40.0,<1.41.0)"] 117 | chime-sdk-meetings = ["mypy-boto3-chime-sdk-meetings (>=1.40.0,<1.41.0)"] 118 | chime-sdk-messaging = ["mypy-boto3-chime-sdk-messaging (>=1.40.0,<1.41.0)"] 119 | chime-sdk-voice = ["mypy-boto3-chime-sdk-voice (>=1.40.0,<1.41.0)"] 120 | cleanrooms = ["mypy-boto3-cleanrooms (>=1.40.0,<1.41.0)"] 121 | cleanroomsml = ["mypy-boto3-cleanroomsml (>=1.40.0,<1.41.0)"] 122 | cloud9 = ["mypy-boto3-cloud9 (>=1.40.0,<1.41.0)"] 123 | cloudcontrol = ["mypy-boto3-cloudcontrol (>=1.40.0,<1.41.0)"] 124 | clouddirectory = ["mypy-boto3-clouddirectory (>=1.40.0,<1.41.0)"] 125 | cloudformation = ["mypy-boto3-cloudformation (>=1.40.0,<1.41.0)"] 126 | cloudfront = ["mypy-boto3-cloudfront (>=1.40.0,<1.41.0)"] 127 | cloudfront-keyvaluestore = ["mypy-boto3-cloudfront-keyvaluestore (>=1.40.0,<1.41.0)"] 128 | cloudhsm = ["mypy-boto3-cloudhsm (>=1.40.0,<1.41.0)"] 129 | cloudhsmv2 = ["mypy-boto3-cloudhsmv2 (>=1.40.0,<1.41.0)"] 130 | cloudsearch = ["mypy-boto3-cloudsearch (>=1.40.0,<1.41.0)"] 131 | cloudsearchdomain = ["mypy-boto3-cloudsearchdomain (>=1.40.0,<1.41.0)"] 132 | cloudtrail = ["mypy-boto3-cloudtrail (>=1.40.0,<1.41.0)"] 133 | cloudtrail-data = ["mypy-boto3-cloudtrail-data (>=1.40.0,<1.41.0)"] 134 | cloudwatch = ["mypy-boto3-cloudwatch (>=1.40.0,<1.41.0)"] 135 | codeartifact = ["mypy-boto3-codeartifact (>=1.40.0,<1.41.0)"] 136 | codebuild = ["mypy-boto3-codebuild (>=1.40.0,<1.41.0)"] 137 | codecatalyst = ["mypy-boto3-codecatalyst (>=1.40.0,<1.41.0)"] 138 | codecommit = ["mypy-boto3-codecommit (>=1.40.0,<1.41.0)"] 139 | codeconnections = ["mypy-boto3-codeconnections (>=1.40.0,<1.41.0)"] 140 | codedeploy = ["mypy-boto3-codedeploy (>=1.40.0,<1.41.0)"] 141 | codeguru-reviewer = ["mypy-boto3-codeguru-reviewer (>=1.40.0,<1.41.0)"] 142 | codeguru-security = ["mypy-boto3-codeguru-security (>=1.40.0,<1.41.0)"] 143 | codeguruprofiler = ["mypy-boto3-codeguruprofiler (>=1.40.0,<1.41.0)"] 144 | codepipeline = ["mypy-boto3-codepipeline (>=1.40.0,<1.41.0)"] 145 | codestar-connections = ["mypy-boto3-codestar-connections (>=1.40.0,<1.41.0)"] 146 | codestar-notifications = ["mypy-boto3-codestar-notifications (>=1.40.0,<1.41.0)"] 147 | cognito-identity = ["mypy-boto3-cognito-identity (>=1.40.0,<1.41.0)"] 148 | cognito-idp = ["mypy-boto3-cognito-idp (>=1.40.0,<1.41.0)"] 149 | cognito-sync = ["mypy-boto3-cognito-sync (>=1.40.0,<1.41.0)"] 150 | comprehend = ["mypy-boto3-comprehend (>=1.40.0,<1.41.0)"] 151 | comprehendmedical = ["mypy-boto3-comprehendmedical (>=1.40.0,<1.41.0)"] 152 | compute-optimizer = ["mypy-boto3-compute-optimizer (>=1.40.0,<1.41.0)"] 153 | config = ["mypy-boto3-config (>=1.40.0,<1.41.0)"] 154 | connect = ["mypy-boto3-connect (>=1.40.0,<1.41.0)"] 155 | connect-contact-lens = ["mypy-boto3-connect-contact-lens (>=1.40.0,<1.41.0)"] 156 | connectcampaigns = ["mypy-boto3-connectcampaigns (>=1.40.0,<1.41.0)"] 157 | connectcampaignsv2 = ["mypy-boto3-connectcampaignsv2 (>=1.40.0,<1.41.0)"] 158 | connectcases = ["mypy-boto3-connectcases (>=1.40.0,<1.41.0)"] 159 | connectparticipant = ["mypy-boto3-connectparticipant (>=1.40.0,<1.41.0)"] 160 | controlcatalog = ["mypy-boto3-controlcatalog (>=1.40.0,<1.41.0)"] 161 | controltower = ["mypy-boto3-controltower (>=1.40.0,<1.41.0)"] 162 | cost-optimization-hub = ["mypy-boto3-cost-optimization-hub (>=1.40.0,<1.41.0)"] 163 | cur = ["mypy-boto3-cur (>=1.40.0,<1.41.0)"] 164 | customer-profiles = ["mypy-boto3-customer-profiles (>=1.40.0,<1.41.0)"] 165 | databrew = ["mypy-boto3-databrew (>=1.40.0,<1.41.0)"] 166 | dataexchange = ["mypy-boto3-dataexchange (>=1.40.0,<1.41.0)"] 167 | datapipeline = ["mypy-boto3-datapipeline (>=1.40.0,<1.41.0)"] 168 | datasync = ["mypy-boto3-datasync (>=1.40.0,<1.41.0)"] 169 | datazone = ["mypy-boto3-datazone (>=1.40.0,<1.41.0)"] 170 | dax = ["mypy-boto3-dax (>=1.40.0,<1.41.0)"] 171 | deadline = ["mypy-boto3-deadline (>=1.40.0,<1.41.0)"] 172 | detective = ["mypy-boto3-detective (>=1.40.0,<1.41.0)"] 173 | devicefarm = ["mypy-boto3-devicefarm (>=1.40.0,<1.41.0)"] 174 | devops-guru = ["mypy-boto3-devops-guru (>=1.40.0,<1.41.0)"] 175 | directconnect = ["mypy-boto3-directconnect (>=1.40.0,<1.41.0)"] 176 | discovery = ["mypy-boto3-discovery (>=1.40.0,<1.41.0)"] 177 | dlm = ["mypy-boto3-dlm (>=1.40.0,<1.41.0)"] 178 | dms = ["mypy-boto3-dms (>=1.40.0,<1.41.0)"] 179 | docdb = ["mypy-boto3-docdb (>=1.40.0,<1.41.0)"] 180 | docdb-elastic = ["mypy-boto3-docdb-elastic (>=1.40.0,<1.41.0)"] 181 | drs = ["mypy-boto3-drs (>=1.40.0,<1.41.0)"] 182 | ds = ["mypy-boto3-ds (>=1.40.0,<1.41.0)"] 183 | ds-data = ["mypy-boto3-ds-data (>=1.40.0,<1.41.0)"] 184 | dsql = ["mypy-boto3-dsql (>=1.40.0,<1.41.0)"] 185 | dynamodb = ["mypy-boto3-dynamodb (>=1.40.0,<1.41.0)"] 186 | dynamodbstreams = ["mypy-boto3-dynamodbstreams (>=1.40.0,<1.41.0)"] 187 | ebs = ["mypy-boto3-ebs (>=1.40.0,<1.41.0)"] 188 | ec2 = ["mypy-boto3-ec2 (>=1.40.0,<1.41.0)"] 189 | ec2-instance-connect = ["mypy-boto3-ec2-instance-connect (>=1.40.0,<1.41.0)"] 190 | ecr = ["mypy-boto3-ecr (>=1.40.0,<1.41.0)"] 191 | ecr-public = ["mypy-boto3-ecr-public (>=1.40.0,<1.41.0)"] 192 | ecs = ["mypy-boto3-ecs (>=1.40.0,<1.41.0)"] 193 | efs = ["mypy-boto3-efs (>=1.40.0,<1.41.0)"] 194 | eks = ["mypy-boto3-eks (>=1.40.0,<1.41.0)"] 195 | eks-auth = ["mypy-boto3-eks-auth (>=1.40.0,<1.41.0)"] 196 | elasticache = ["mypy-boto3-elasticache (>=1.40.0,<1.41.0)"] 197 | elasticbeanstalk = ["mypy-boto3-elasticbeanstalk (>=1.40.0,<1.41.0)"] 198 | elastictranscoder = ["mypy-boto3-elastictranscoder (>=1.40.0,<1.41.0)"] 199 | elb = ["mypy-boto3-elb (>=1.40.0,<1.41.0)"] 200 | elbv2 = ["mypy-boto3-elbv2 (>=1.40.0,<1.41.0)"] 201 | emr = ["mypy-boto3-emr (>=1.40.0,<1.41.0)"] 202 | emr-containers = ["mypy-boto3-emr-containers (>=1.40.0,<1.41.0)"] 203 | emr-serverless = ["mypy-boto3-emr-serverless (>=1.40.0,<1.41.0)"] 204 | entityresolution = ["mypy-boto3-entityresolution (>=1.40.0,<1.41.0)"] 205 | es = ["mypy-boto3-es (>=1.40.0,<1.41.0)"] 206 | essential = ["mypy-boto3-cloudformation (>=1.40.0,<1.41.0)", "mypy-boto3-dynamodb (>=1.40.0,<1.41.0)", "mypy-boto3-ec2 (>=1.40.0,<1.41.0)", "mypy-boto3-lambda (>=1.40.0,<1.41.0)", "mypy-boto3-rds (>=1.40.0,<1.41.0)", "mypy-boto3-s3 (>=1.40.0,<1.41.0)", "mypy-boto3-sqs (>=1.40.0,<1.41.0)"] 207 | events = ["mypy-boto3-events (>=1.40.0,<1.41.0)"] 208 | evidently = ["mypy-boto3-evidently (>=1.40.0,<1.41.0)"] 209 | evs = ["mypy-boto3-evs (>=1.40.0,<1.41.0)"] 210 | finspace = ["mypy-boto3-finspace (>=1.40.0,<1.41.0)"] 211 | finspace-data = ["mypy-boto3-finspace-data (>=1.40.0,<1.41.0)"] 212 | firehose = ["mypy-boto3-firehose (>=1.40.0,<1.41.0)"] 213 | fis = ["mypy-boto3-fis (>=1.40.0,<1.41.0)"] 214 | fms = ["mypy-boto3-fms (>=1.40.0,<1.41.0)"] 215 | forecast = ["mypy-boto3-forecast (>=1.40.0,<1.41.0)"] 216 | forecastquery = ["mypy-boto3-forecastquery (>=1.40.0,<1.41.0)"] 217 | frauddetector = ["mypy-boto3-frauddetector (>=1.40.0,<1.41.0)"] 218 | freetier = ["mypy-boto3-freetier (>=1.40.0,<1.41.0)"] 219 | fsx = ["mypy-boto3-fsx (>=1.40.0,<1.41.0)"] 220 | full = ["boto3-stubs-full (>=1.40.0,<1.41.0)"] 221 | gamelift = ["mypy-boto3-gamelift (>=1.40.0,<1.41.0)"] 222 | gameliftstreams = ["mypy-boto3-gameliftstreams (>=1.40.0,<1.41.0)"] 223 | geo-maps = ["mypy-boto3-geo-maps (>=1.40.0,<1.41.0)"] 224 | geo-places = ["mypy-boto3-geo-places (>=1.40.0,<1.41.0)"] 225 | geo-routes = ["mypy-boto3-geo-routes (>=1.40.0,<1.41.0)"] 226 | glacier = ["mypy-boto3-glacier (>=1.40.0,<1.41.0)"] 227 | globalaccelerator = ["mypy-boto3-globalaccelerator (>=1.40.0,<1.41.0)"] 228 | glue = ["mypy-boto3-glue (>=1.40.0,<1.41.0)"] 229 | grafana = ["mypy-boto3-grafana (>=1.40.0,<1.41.0)"] 230 | greengrass = ["mypy-boto3-greengrass (>=1.40.0,<1.41.0)"] 231 | greengrassv2 = ["mypy-boto3-greengrassv2 (>=1.40.0,<1.41.0)"] 232 | groundstation = ["mypy-boto3-groundstation (>=1.40.0,<1.41.0)"] 233 | guardduty = ["mypy-boto3-guardduty (>=1.40.0,<1.41.0)"] 234 | health = ["mypy-boto3-health (>=1.40.0,<1.41.0)"] 235 | healthlake = ["mypy-boto3-healthlake (>=1.40.0,<1.41.0)"] 236 | iam = ["mypy-boto3-iam (>=1.40.0,<1.41.0)"] 237 | identitystore = ["mypy-boto3-identitystore (>=1.40.0,<1.41.0)"] 238 | imagebuilder = ["mypy-boto3-imagebuilder (>=1.40.0,<1.41.0)"] 239 | importexport = ["mypy-boto3-importexport (>=1.40.0,<1.41.0)"] 240 | inspector = ["mypy-boto3-inspector (>=1.40.0,<1.41.0)"] 241 | inspector-scan = ["mypy-boto3-inspector-scan (>=1.40.0,<1.41.0)"] 242 | inspector2 = ["mypy-boto3-inspector2 (>=1.40.0,<1.41.0)"] 243 | internetmonitor = ["mypy-boto3-internetmonitor (>=1.40.0,<1.41.0)"] 244 | invoicing = ["mypy-boto3-invoicing (>=1.40.0,<1.41.0)"] 245 | iot = ["mypy-boto3-iot (>=1.40.0,<1.41.0)"] 246 | iot-data = ["mypy-boto3-iot-data (>=1.40.0,<1.41.0)"] 247 | iot-jobs-data = ["mypy-boto3-iot-jobs-data (>=1.40.0,<1.41.0)"] 248 | iot-managed-integrations = ["mypy-boto3-iot-managed-integrations (>=1.40.0,<1.41.0)"] 249 | iotanalytics = ["mypy-boto3-iotanalytics (>=1.40.0,<1.41.0)"] 250 | iotdeviceadvisor = ["mypy-boto3-iotdeviceadvisor (>=1.40.0,<1.41.0)"] 251 | iotevents = ["mypy-boto3-iotevents (>=1.40.0,<1.41.0)"] 252 | iotevents-data = ["mypy-boto3-iotevents-data (>=1.40.0,<1.41.0)"] 253 | iotfleetwise = ["mypy-boto3-iotfleetwise (>=1.40.0,<1.41.0)"] 254 | iotsecuretunneling = ["mypy-boto3-iotsecuretunneling (>=1.40.0,<1.41.0)"] 255 | iotsitewise = ["mypy-boto3-iotsitewise (>=1.40.0,<1.41.0)"] 256 | iotthingsgraph = ["mypy-boto3-iotthingsgraph (>=1.40.0,<1.41.0)"] 257 | iottwinmaker = ["mypy-boto3-iottwinmaker (>=1.40.0,<1.41.0)"] 258 | iotwireless = ["mypy-boto3-iotwireless (>=1.40.0,<1.41.0)"] 259 | ivs = ["mypy-boto3-ivs (>=1.40.0,<1.41.0)"] 260 | ivs-realtime = ["mypy-boto3-ivs-realtime (>=1.40.0,<1.41.0)"] 261 | ivschat = ["mypy-boto3-ivschat (>=1.40.0,<1.41.0)"] 262 | kafka = ["mypy-boto3-kafka (>=1.40.0,<1.41.0)"] 263 | kafkaconnect = ["mypy-boto3-kafkaconnect (>=1.40.0,<1.41.0)"] 264 | kendra = ["mypy-boto3-kendra (>=1.40.0,<1.41.0)"] 265 | kendra-ranking = ["mypy-boto3-kendra-ranking (>=1.40.0,<1.41.0)"] 266 | keyspaces = ["mypy-boto3-keyspaces (>=1.40.0,<1.41.0)"] 267 | keyspacesstreams = ["mypy-boto3-keyspacesstreams (>=1.40.0,<1.41.0)"] 268 | kinesis = ["mypy-boto3-kinesis (>=1.40.0,<1.41.0)"] 269 | kinesis-video-archived-media = ["mypy-boto3-kinesis-video-archived-media (>=1.40.0,<1.41.0)"] 270 | kinesis-video-media = ["mypy-boto3-kinesis-video-media (>=1.40.0,<1.41.0)"] 271 | kinesis-video-signaling = ["mypy-boto3-kinesis-video-signaling (>=1.40.0,<1.41.0)"] 272 | kinesis-video-webrtc-storage = ["mypy-boto3-kinesis-video-webrtc-storage (>=1.40.0,<1.41.0)"] 273 | kinesisanalytics = ["mypy-boto3-kinesisanalytics (>=1.40.0,<1.41.0)"] 274 | kinesisanalyticsv2 = ["mypy-boto3-kinesisanalyticsv2 (>=1.40.0,<1.41.0)"] 275 | kinesisvideo = ["mypy-boto3-kinesisvideo (>=1.40.0,<1.41.0)"] 276 | kms = ["mypy-boto3-kms (>=1.40.0,<1.41.0)"] 277 | lakeformation = ["mypy-boto3-lakeformation (>=1.40.0,<1.41.0)"] 278 | lambda = ["mypy-boto3-lambda (>=1.40.0,<1.41.0)"] 279 | launch-wizard = ["mypy-boto3-launch-wizard (>=1.40.0,<1.41.0)"] 280 | lex-models = ["mypy-boto3-lex-models (>=1.40.0,<1.41.0)"] 281 | lex-runtime = ["mypy-boto3-lex-runtime (>=1.40.0,<1.41.0)"] 282 | lexv2-models = ["mypy-boto3-lexv2-models (>=1.40.0,<1.41.0)"] 283 | lexv2-runtime = ["mypy-boto3-lexv2-runtime (>=1.40.0,<1.41.0)"] 284 | license-manager = ["mypy-boto3-license-manager (>=1.40.0,<1.41.0)"] 285 | license-manager-linux-subscriptions = ["mypy-boto3-license-manager-linux-subscriptions (>=1.40.0,<1.41.0)"] 286 | license-manager-user-subscriptions = ["mypy-boto3-license-manager-user-subscriptions (>=1.40.0,<1.41.0)"] 287 | lightsail = ["mypy-boto3-lightsail (>=1.40.0,<1.41.0)"] 288 | location = ["mypy-boto3-location (>=1.40.0,<1.41.0)"] 289 | logs = ["mypy-boto3-logs (>=1.40.0,<1.41.0)"] 290 | lookoutequipment = ["mypy-boto3-lookoutequipment (>=1.40.0,<1.41.0)"] 291 | m2 = ["mypy-boto3-m2 (>=1.40.0,<1.41.0)"] 292 | machinelearning = ["mypy-boto3-machinelearning (>=1.40.0,<1.41.0)"] 293 | macie2 = ["mypy-boto3-macie2 (>=1.40.0,<1.41.0)"] 294 | mailmanager = ["mypy-boto3-mailmanager (>=1.40.0,<1.41.0)"] 295 | managedblockchain = ["mypy-boto3-managedblockchain (>=1.40.0,<1.41.0)"] 296 | managedblockchain-query = ["mypy-boto3-managedblockchain-query (>=1.40.0,<1.41.0)"] 297 | marketplace-agreement = ["mypy-boto3-marketplace-agreement (>=1.40.0,<1.41.0)"] 298 | marketplace-catalog = ["mypy-boto3-marketplace-catalog (>=1.40.0,<1.41.0)"] 299 | marketplace-deployment = ["mypy-boto3-marketplace-deployment (>=1.40.0,<1.41.0)"] 300 | marketplace-entitlement = ["mypy-boto3-marketplace-entitlement (>=1.40.0,<1.41.0)"] 301 | marketplace-reporting = ["mypy-boto3-marketplace-reporting (>=1.40.0,<1.41.0)"] 302 | marketplacecommerceanalytics = ["mypy-boto3-marketplacecommerceanalytics (>=1.40.0,<1.41.0)"] 303 | mediaconnect = ["mypy-boto3-mediaconnect (>=1.40.0,<1.41.0)"] 304 | mediaconvert = ["mypy-boto3-mediaconvert (>=1.40.0,<1.41.0)"] 305 | medialive = ["mypy-boto3-medialive (>=1.40.0,<1.41.0)"] 306 | mediapackage = ["mypy-boto3-mediapackage (>=1.40.0,<1.41.0)"] 307 | mediapackage-vod = ["mypy-boto3-mediapackage-vod (>=1.40.0,<1.41.0)"] 308 | mediapackagev2 = ["mypy-boto3-mediapackagev2 (>=1.40.0,<1.41.0)"] 309 | mediastore = ["mypy-boto3-mediastore (>=1.40.0,<1.41.0)"] 310 | mediastore-data = ["mypy-boto3-mediastore-data (>=1.40.0,<1.41.0)"] 311 | mediatailor = ["mypy-boto3-mediatailor (>=1.40.0,<1.41.0)"] 312 | medical-imaging = ["mypy-boto3-medical-imaging (>=1.40.0,<1.41.0)"] 313 | memorydb = ["mypy-boto3-memorydb (>=1.40.0,<1.41.0)"] 314 | meteringmarketplace = ["mypy-boto3-meteringmarketplace (>=1.40.0,<1.41.0)"] 315 | mgh = ["mypy-boto3-mgh (>=1.40.0,<1.41.0)"] 316 | mgn = ["mypy-boto3-mgn (>=1.40.0,<1.41.0)"] 317 | migration-hub-refactor-spaces = ["mypy-boto3-migration-hub-refactor-spaces (>=1.40.0,<1.41.0)"] 318 | migrationhub-config = ["mypy-boto3-migrationhub-config (>=1.40.0,<1.41.0)"] 319 | migrationhuborchestrator = ["mypy-boto3-migrationhuborchestrator (>=1.40.0,<1.41.0)"] 320 | migrationhubstrategy = ["mypy-boto3-migrationhubstrategy (>=1.40.0,<1.41.0)"] 321 | mpa = ["mypy-boto3-mpa (>=1.40.0,<1.41.0)"] 322 | mq = ["mypy-boto3-mq (>=1.40.0,<1.41.0)"] 323 | mturk = ["mypy-boto3-mturk (>=1.40.0,<1.41.0)"] 324 | mwaa = ["mypy-boto3-mwaa (>=1.40.0,<1.41.0)"] 325 | neptune = ["mypy-boto3-neptune (>=1.40.0,<1.41.0)"] 326 | neptune-graph = ["mypy-boto3-neptune-graph (>=1.40.0,<1.41.0)"] 327 | neptunedata = ["mypy-boto3-neptunedata (>=1.40.0,<1.41.0)"] 328 | network-firewall = ["mypy-boto3-network-firewall (>=1.40.0,<1.41.0)"] 329 | networkflowmonitor = ["mypy-boto3-networkflowmonitor (>=1.40.0,<1.41.0)"] 330 | networkmanager = ["mypy-boto3-networkmanager (>=1.40.0,<1.41.0)"] 331 | networkmonitor = ["mypy-boto3-networkmonitor (>=1.40.0,<1.41.0)"] 332 | notifications = ["mypy-boto3-notifications (>=1.40.0,<1.41.0)"] 333 | notificationscontacts = ["mypy-boto3-notificationscontacts (>=1.40.0,<1.41.0)"] 334 | oam = ["mypy-boto3-oam (>=1.40.0,<1.41.0)"] 335 | observabilityadmin = ["mypy-boto3-observabilityadmin (>=1.40.0,<1.41.0)"] 336 | odb = ["mypy-boto3-odb (>=1.40.0,<1.41.0)"] 337 | omics = ["mypy-boto3-omics (>=1.40.0,<1.41.0)"] 338 | opensearch = ["mypy-boto3-opensearch (>=1.40.0,<1.41.0)"] 339 | opensearchserverless = ["mypy-boto3-opensearchserverless (>=1.40.0,<1.41.0)"] 340 | organizations = ["mypy-boto3-organizations (>=1.40.0,<1.41.0)"] 341 | osis = ["mypy-boto3-osis (>=1.40.0,<1.41.0)"] 342 | outposts = ["mypy-boto3-outposts (>=1.40.0,<1.41.0)"] 343 | panorama = ["mypy-boto3-panorama (>=1.40.0,<1.41.0)"] 344 | partnercentral-selling = ["mypy-boto3-partnercentral-selling (>=1.40.0,<1.41.0)"] 345 | payment-cryptography = ["mypy-boto3-payment-cryptography (>=1.40.0,<1.41.0)"] 346 | payment-cryptography-data = ["mypy-boto3-payment-cryptography-data (>=1.40.0,<1.41.0)"] 347 | pca-connector-ad = ["mypy-boto3-pca-connector-ad (>=1.40.0,<1.41.0)"] 348 | pca-connector-scep = ["mypy-boto3-pca-connector-scep (>=1.40.0,<1.41.0)"] 349 | pcs = ["mypy-boto3-pcs (>=1.40.0,<1.41.0)"] 350 | personalize = ["mypy-boto3-personalize (>=1.40.0,<1.41.0)"] 351 | personalize-events = ["mypy-boto3-personalize-events (>=1.40.0,<1.41.0)"] 352 | personalize-runtime = ["mypy-boto3-personalize-runtime (>=1.40.0,<1.41.0)"] 353 | pi = ["mypy-boto3-pi (>=1.40.0,<1.41.0)"] 354 | pinpoint = ["mypy-boto3-pinpoint (>=1.40.0,<1.41.0)"] 355 | pinpoint-email = ["mypy-boto3-pinpoint-email (>=1.40.0,<1.41.0)"] 356 | pinpoint-sms-voice = ["mypy-boto3-pinpoint-sms-voice (>=1.40.0,<1.41.0)"] 357 | pinpoint-sms-voice-v2 = ["mypy-boto3-pinpoint-sms-voice-v2 (>=1.40.0,<1.41.0)"] 358 | pipes = ["mypy-boto3-pipes (>=1.40.0,<1.41.0)"] 359 | polly = ["mypy-boto3-polly (>=1.40.0,<1.41.0)"] 360 | pricing = ["mypy-boto3-pricing (>=1.40.0,<1.41.0)"] 361 | proton = ["mypy-boto3-proton (>=1.40.0,<1.41.0)"] 362 | qapps = ["mypy-boto3-qapps (>=1.40.0,<1.41.0)"] 363 | qbusiness = ["mypy-boto3-qbusiness (>=1.40.0,<1.41.0)"] 364 | qconnect = ["mypy-boto3-qconnect (>=1.40.0,<1.41.0)"] 365 | quicksight = ["mypy-boto3-quicksight (>=1.40.0,<1.41.0)"] 366 | ram = ["mypy-boto3-ram (>=1.40.0,<1.41.0)"] 367 | rbin = ["mypy-boto3-rbin (>=1.40.0,<1.41.0)"] 368 | rds = ["mypy-boto3-rds (>=1.40.0,<1.41.0)"] 369 | rds-data = ["mypy-boto3-rds-data (>=1.40.0,<1.41.0)"] 370 | redshift = ["mypy-boto3-redshift (>=1.40.0,<1.41.0)"] 371 | redshift-data = ["mypy-boto3-redshift-data (>=1.40.0,<1.41.0)"] 372 | redshift-serverless = ["mypy-boto3-redshift-serverless (>=1.40.0,<1.41.0)"] 373 | rekognition = ["mypy-boto3-rekognition (>=1.40.0,<1.41.0)"] 374 | repostspace = ["mypy-boto3-repostspace (>=1.40.0,<1.41.0)"] 375 | resiliencehub = ["mypy-boto3-resiliencehub (>=1.40.0,<1.41.0)"] 376 | resource-explorer-2 = ["mypy-boto3-resource-explorer-2 (>=1.40.0,<1.41.0)"] 377 | resource-groups = ["mypy-boto3-resource-groups (>=1.40.0,<1.41.0)"] 378 | resourcegroupstaggingapi = ["mypy-boto3-resourcegroupstaggingapi (>=1.40.0,<1.41.0)"] 379 | rolesanywhere = ["mypy-boto3-rolesanywhere (>=1.40.0,<1.41.0)"] 380 | route53 = ["mypy-boto3-route53 (>=1.40.0,<1.41.0)"] 381 | route53-recovery-cluster = ["mypy-boto3-route53-recovery-cluster (>=1.40.0,<1.41.0)"] 382 | route53-recovery-control-config = ["mypy-boto3-route53-recovery-control-config (>=1.40.0,<1.41.0)"] 383 | route53-recovery-readiness = ["mypy-boto3-route53-recovery-readiness (>=1.40.0,<1.41.0)"] 384 | route53domains = ["mypy-boto3-route53domains (>=1.40.0,<1.41.0)"] 385 | route53profiles = ["mypy-boto3-route53profiles (>=1.40.0,<1.41.0)"] 386 | route53resolver = ["mypy-boto3-route53resolver (>=1.40.0,<1.41.0)"] 387 | rtbfabric = ["mypy-boto3-rtbfabric (>=1.40.0,<1.41.0)"] 388 | rum = ["mypy-boto3-rum (>=1.40.0,<1.41.0)"] 389 | s3 = ["mypy-boto3-s3 (>=1.40.0,<1.41.0)"] 390 | s3control = ["mypy-boto3-s3control (>=1.40.0,<1.41.0)"] 391 | s3outposts = ["mypy-boto3-s3outposts (>=1.40.0,<1.41.0)"] 392 | s3tables = ["mypy-boto3-s3tables (>=1.40.0,<1.41.0)"] 393 | s3vectors = ["mypy-boto3-s3vectors (>=1.40.0,<1.41.0)"] 394 | sagemaker = ["mypy-boto3-sagemaker (>=1.40.0,<1.41.0)"] 395 | sagemaker-a2i-runtime = ["mypy-boto3-sagemaker-a2i-runtime (>=1.40.0,<1.41.0)"] 396 | sagemaker-edge = ["mypy-boto3-sagemaker-edge (>=1.40.0,<1.41.0)"] 397 | sagemaker-featurestore-runtime = ["mypy-boto3-sagemaker-featurestore-runtime (>=1.40.0,<1.41.0)"] 398 | sagemaker-geospatial = ["mypy-boto3-sagemaker-geospatial (>=1.40.0,<1.41.0)"] 399 | sagemaker-metrics = ["mypy-boto3-sagemaker-metrics (>=1.40.0,<1.41.0)"] 400 | sagemaker-runtime = ["mypy-boto3-sagemaker-runtime (>=1.40.0,<1.41.0)"] 401 | savingsplans = ["mypy-boto3-savingsplans (>=1.40.0,<1.41.0)"] 402 | scheduler = ["mypy-boto3-scheduler (>=1.40.0,<1.41.0)"] 403 | schemas = ["mypy-boto3-schemas (>=1.40.0,<1.41.0)"] 404 | sdb = ["mypy-boto3-sdb (>=1.40.0,<1.41.0)"] 405 | secretsmanager = ["mypy-boto3-secretsmanager (>=1.40.0,<1.41.0)"] 406 | security-ir = ["mypy-boto3-security-ir (>=1.40.0,<1.41.0)"] 407 | securityhub = ["mypy-boto3-securityhub (>=1.40.0,<1.41.0)"] 408 | securitylake = ["mypy-boto3-securitylake (>=1.40.0,<1.41.0)"] 409 | serverlessrepo = ["mypy-boto3-serverlessrepo (>=1.40.0,<1.41.0)"] 410 | service-quotas = ["mypy-boto3-service-quotas (>=1.40.0,<1.41.0)"] 411 | servicecatalog = ["mypy-boto3-servicecatalog (>=1.40.0,<1.41.0)"] 412 | servicecatalog-appregistry = ["mypy-boto3-servicecatalog-appregistry (>=1.40.0,<1.41.0)"] 413 | servicediscovery = ["mypy-boto3-servicediscovery (>=1.40.0,<1.41.0)"] 414 | ses = ["mypy-boto3-ses (>=1.40.0,<1.41.0)"] 415 | sesv2 = ["mypy-boto3-sesv2 (>=1.40.0,<1.41.0)"] 416 | shield = ["mypy-boto3-shield (>=1.40.0,<1.41.0)"] 417 | signer = ["mypy-boto3-signer (>=1.40.0,<1.41.0)"] 418 | simspaceweaver = ["mypy-boto3-simspaceweaver (>=1.40.0,<1.41.0)"] 419 | snow-device-management = ["mypy-boto3-snow-device-management (>=1.40.0,<1.41.0)"] 420 | snowball = ["mypy-boto3-snowball (>=1.40.0,<1.41.0)"] 421 | sns = ["mypy-boto3-sns (>=1.40.0,<1.41.0)"] 422 | socialmessaging = ["mypy-boto3-socialmessaging (>=1.40.0,<1.41.0)"] 423 | sqs = ["mypy-boto3-sqs (>=1.40.0,<1.41.0)"] 424 | ssm = ["mypy-boto3-ssm (>=1.40.0,<1.41.0)"] 425 | ssm-contacts = ["mypy-boto3-ssm-contacts (>=1.40.0,<1.41.0)"] 426 | ssm-guiconnect = ["mypy-boto3-ssm-guiconnect (>=1.40.0,<1.41.0)"] 427 | ssm-incidents = ["mypy-boto3-ssm-incidents (>=1.40.0,<1.41.0)"] 428 | ssm-quicksetup = ["mypy-boto3-ssm-quicksetup (>=1.40.0,<1.41.0)"] 429 | ssm-sap = ["mypy-boto3-ssm-sap (>=1.40.0,<1.41.0)"] 430 | sso = ["mypy-boto3-sso (>=1.40.0,<1.41.0)"] 431 | sso-admin = ["mypy-boto3-sso-admin (>=1.40.0,<1.41.0)"] 432 | sso-oidc = ["mypy-boto3-sso-oidc (>=1.40.0,<1.41.0)"] 433 | stepfunctions = ["mypy-boto3-stepfunctions (>=1.40.0,<1.41.0)"] 434 | storagegateway = ["mypy-boto3-storagegateway (>=1.40.0,<1.41.0)"] 435 | sts = ["mypy-boto3-sts (>=1.40.0,<1.41.0)"] 436 | supplychain = ["mypy-boto3-supplychain (>=1.40.0,<1.41.0)"] 437 | support = ["mypy-boto3-support (>=1.40.0,<1.41.0)"] 438 | support-app = ["mypy-boto3-support-app (>=1.40.0,<1.41.0)"] 439 | swf = ["mypy-boto3-swf (>=1.40.0,<1.41.0)"] 440 | synthetics = ["mypy-boto3-synthetics (>=1.40.0,<1.41.0)"] 441 | taxsettings = ["mypy-boto3-taxsettings (>=1.40.0,<1.41.0)"] 442 | textract = ["mypy-boto3-textract (>=1.40.0,<1.41.0)"] 443 | timestream-influxdb = ["mypy-boto3-timestream-influxdb (>=1.40.0,<1.41.0)"] 444 | timestream-query = ["mypy-boto3-timestream-query (>=1.40.0,<1.41.0)"] 445 | timestream-write = ["mypy-boto3-timestream-write (>=1.40.0,<1.41.0)"] 446 | tnb = ["mypy-boto3-tnb (>=1.40.0,<1.41.0)"] 447 | transcribe = ["mypy-boto3-transcribe (>=1.40.0,<1.41.0)"] 448 | transfer = ["mypy-boto3-transfer (>=1.40.0,<1.41.0)"] 449 | translate = ["mypy-boto3-translate (>=1.40.0,<1.41.0)"] 450 | trustedadvisor = ["mypy-boto3-trustedadvisor (>=1.40.0,<1.41.0)"] 451 | verifiedpermissions = ["mypy-boto3-verifiedpermissions (>=1.40.0,<1.41.0)"] 452 | voice-id = ["mypy-boto3-voice-id (>=1.40.0,<1.41.0)"] 453 | vpc-lattice = ["mypy-boto3-vpc-lattice (>=1.40.0,<1.41.0)"] 454 | waf = ["mypy-boto3-waf (>=1.40.0,<1.41.0)"] 455 | waf-regional = ["mypy-boto3-waf-regional (>=1.40.0,<1.41.0)"] 456 | wafv2 = ["mypy-boto3-wafv2 (>=1.40.0,<1.41.0)"] 457 | wellarchitected = ["mypy-boto3-wellarchitected (>=1.40.0,<1.41.0)"] 458 | wisdom = ["mypy-boto3-wisdom (>=1.40.0,<1.41.0)"] 459 | workdocs = ["mypy-boto3-workdocs (>=1.40.0,<1.41.0)"] 460 | workmail = ["mypy-boto3-workmail (>=1.40.0,<1.41.0)"] 461 | workmailmessageflow = ["mypy-boto3-workmailmessageflow (>=1.40.0,<1.41.0)"] 462 | workspaces = ["mypy-boto3-workspaces (>=1.40.0,<1.41.0)"] 463 | workspaces-instances = ["mypy-boto3-workspaces-instances (>=1.40.0,<1.41.0)"] 464 | workspaces-thin-client = ["mypy-boto3-workspaces-thin-client (>=1.40.0,<1.41.0)"] 465 | workspaces-web = ["mypy-boto3-workspaces-web (>=1.40.0,<1.41.0)"] 466 | xray = ["mypy-boto3-xray (>=1.40.0,<1.41.0)"] 467 | 468 | [[package]] 469 | name = "botocore" 470 | version = "1.40.68" 471 | description = "Low-level, data-driven core of boto 3." 472 | optional = false 473 | python-versions = ">=3.9" 474 | files = [ 475 | {file = "botocore-1.40.68-py3-none-any.whl", hash = "sha256:9d514f9c9054e1af055f2cbe9e0d6771d407a600206d45a01b54d5f09538fecb"}, 476 | {file = "botocore-1.40.68.tar.gz", hash = "sha256:28f41b463d9f012a711ee8b61d4e26cd14ee3b450b816d5dee849aa79155e856"}, 477 | ] 478 | 479 | [package.dependencies] 480 | jmespath = ">=0.7.1,<2.0.0" 481 | python-dateutil = ">=2.1,<3.0.0" 482 | urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} 483 | 484 | [package.extras] 485 | crt = ["awscrt (==0.27.6)"] 486 | 487 | [[package]] 488 | name = "botocore-stubs" 489 | version = "1.35.67" 490 | description = "Type annotations and code completion for botocore" 491 | optional = false 492 | python-versions = ">=3.8" 493 | files = [ 494 | {file = "botocore_stubs-1.35.67-py3-none-any.whl", hash = "sha256:2c10c6c34473e33c5ff1569aef44b8c4ce4afcb0a502a99f7f192dc31006e9f2"}, 495 | {file = "botocore_stubs-1.35.67.tar.gz", hash = "sha256:21886510f4fd8f5804e4bb83661ba0e005e4c5236b89f70dd428e6cea68b0ee6"}, 496 | ] 497 | 498 | [package.dependencies] 499 | types-awscrt = "*" 500 | 501 | [package.extras] 502 | botocore = ["botocore"] 503 | 504 | [[package]] 505 | name = "click" 506 | version = "8.3.0" 507 | description = "Composable command line interface toolkit" 508 | optional = false 509 | python-versions = ">=3.10" 510 | files = [ 511 | {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"}, 512 | {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"}, 513 | ] 514 | 515 | [package.dependencies] 516 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 517 | 518 | [[package]] 519 | name = "colorama" 520 | version = "0.4.6" 521 | description = "Cross-platform colored terminal text." 522 | optional = false 523 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 524 | files = [ 525 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 526 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 527 | ] 528 | 529 | [[package]] 530 | name = "exceptiongroup" 531 | version = "1.2.2" 532 | description = "Backport of PEP 654 (exception groups)" 533 | optional = false 534 | python-versions = ">=3.7" 535 | files = [ 536 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 537 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 538 | ] 539 | 540 | [package.extras] 541 | test = ["pytest (>=6)"] 542 | 543 | [[package]] 544 | name = "iniconfig" 545 | version = "2.0.0" 546 | description = "brain-dead simple config-ini parsing" 547 | optional = false 548 | python-versions = ">=3.7" 549 | files = [ 550 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 551 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 552 | ] 553 | 554 | [[package]] 555 | name = "jmespath" 556 | version = "1.0.1" 557 | description = "JSON Matching Expressions" 558 | optional = false 559 | python-versions = ">=3.7" 560 | files = [ 561 | {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, 562 | {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, 563 | ] 564 | 565 | [[package]] 566 | name = "mypy" 567 | version = "1.18.2" 568 | description = "Optional static typing for Python" 569 | optional = false 570 | python-versions = ">=3.9" 571 | files = [ 572 | {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"}, 573 | {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"}, 574 | {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"}, 575 | {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"}, 576 | {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"}, 577 | {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"}, 578 | {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"}, 579 | {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"}, 580 | {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"}, 581 | {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"}, 582 | {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"}, 583 | {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"}, 584 | {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"}, 585 | {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"}, 586 | {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"}, 587 | {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"}, 588 | {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"}, 589 | {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"}, 590 | {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"}, 591 | {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"}, 592 | {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"}, 593 | {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"}, 594 | {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"}, 595 | {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"}, 596 | {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"}, 597 | {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"}, 598 | {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"}, 599 | {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"}, 600 | {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"}, 601 | {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"}, 602 | {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"}, 603 | {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"}, 604 | {file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"}, 605 | {file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"}, 606 | {file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"}, 607 | {file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"}, 608 | {file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"}, 609 | {file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"}, 610 | ] 611 | 612 | [package.dependencies] 613 | mypy_extensions = ">=1.0.0" 614 | pathspec = ">=0.9.0" 615 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 616 | typing_extensions = ">=4.6.0" 617 | 618 | [package.extras] 619 | dmypy = ["psutil (>=4.0)"] 620 | faster-cache = ["orjson"] 621 | install-types = ["pip"] 622 | mypyc = ["setuptools (>=50)"] 623 | reports = ["lxml"] 624 | 625 | [[package]] 626 | name = "mypy-boto3-cloudformation" 627 | version = "1.40.0" 628 | description = "Type annotations for boto3 CloudFormation 1.40.0 service generated with mypy-boto3-builder 8.11.0" 629 | optional = false 630 | python-versions = ">=3.8" 631 | files = [ 632 | {file = "mypy_boto3_cloudformation-1.40.0-py3-none-any.whl", hash = "sha256:3daa2b10307f4763cb9479e541b1d45742a79a3c598f1a577389c5735fa8ad10"}, 633 | {file = "mypy_boto3_cloudformation-1.40.0.tar.gz", hash = "sha256:a0beaae56355fb3e5eb4439d65a919a9e61f6ea2f69ffbf0a03fd6b45ad895f0"}, 634 | ] 635 | 636 | [package.dependencies] 637 | typing-extensions = {version = "*", markers = "python_version < \"3.12\""} 638 | 639 | [[package]] 640 | name = "mypy-boto3-dynamodb" 641 | version = "1.40.0" 642 | description = "Type annotations for boto3 DynamoDB 1.40.0 service generated with mypy-boto3-builder 8.11.0" 643 | optional = false 644 | python-versions = ">=3.8" 645 | files = [ 646 | {file = "mypy_boto3_dynamodb-1.40.0-py3-none-any.whl", hash = "sha256:b7b0c02e58d1c2323378a9c648c39c68bef867cf7da2721ea257e1c6aaa3d229"}, 647 | {file = "mypy_boto3_dynamodb-1.40.0.tar.gz", hash = "sha256:97f65006a1706f7cbdf53ad1c3a9914e10b53754194db4ad12004eca7c376b4e"}, 648 | ] 649 | 650 | [package.dependencies] 651 | typing-extensions = {version = "*", markers = "python_version < \"3.12\""} 652 | 653 | [[package]] 654 | name = "mypy-boto3-ec2" 655 | version = "1.40.0" 656 | description = "Type annotations for boto3 EC2 1.40.0 service generated with mypy-boto3-builder 8.11.0" 657 | optional = false 658 | python-versions = ">=3.8" 659 | files = [ 660 | {file = "mypy_boto3_ec2-1.40.0-py3-none-any.whl", hash = "sha256:6a5cb04a034a07963bbf397cd95a78c61ae6cbd1b18e9869b73a624d9075ee58"}, 661 | {file = "mypy_boto3_ec2-1.40.0.tar.gz", hash = "sha256:8b23c0915a5f9eacf6457d7692550e4c7d8d6853bfb407e25855cb17e14719ed"}, 662 | ] 663 | 664 | [package.dependencies] 665 | typing-extensions = {version = "*", markers = "python_version < \"3.12\""} 666 | 667 | [[package]] 668 | name = "mypy-boto3-lambda" 669 | version = "1.40.0" 670 | description = "Type annotations for boto3 Lambda 1.40.0 service generated with mypy-boto3-builder 8.11.0" 671 | optional = false 672 | python-versions = ">=3.8" 673 | files = [ 674 | {file = "mypy_boto3_lambda-1.40.0-py3-none-any.whl", hash = "sha256:41a8ad2342dd9fb3af3f89327ce44a636066ccb4fe8d5fac1f897c7e8e5b16b9"}, 675 | {file = "mypy_boto3_lambda-1.40.0.tar.gz", hash = "sha256:0cb0d3ef708ad6bcff8e4bd968c2e6f30e94f157831abeeca01fbce95d38bfa1"}, 676 | ] 677 | 678 | [package.dependencies] 679 | typing-extensions = {version = "*", markers = "python_version < \"3.12\""} 680 | 681 | [[package]] 682 | name = "mypy-boto3-rds" 683 | version = "1.40.0" 684 | description = "Type annotations for boto3 RDS 1.40.0 service generated with mypy-boto3-builder 8.11.0" 685 | optional = false 686 | python-versions = ">=3.8" 687 | files = [ 688 | {file = "mypy_boto3_rds-1.40.0-py3-none-any.whl", hash = "sha256:a7a6d626cef970eb9a71bfe906ea878aed9d366f59be30aec8b2b7d4cd435ada"}, 689 | {file = "mypy_boto3_rds-1.40.0.tar.gz", hash = "sha256:1e327847d71929bc5358c3a27a1c881506e680589af0049ec0365d147442136d"}, 690 | ] 691 | 692 | [package.dependencies] 693 | typing-extensions = {version = "*", markers = "python_version < \"3.12\""} 694 | 695 | [[package]] 696 | name = "mypy-boto3-s3" 697 | version = "1.40.0" 698 | description = "Type annotations for boto3 S3 1.40.0 service generated with mypy-boto3-builder 8.11.0" 699 | optional = false 700 | python-versions = ">=3.8" 701 | files = [ 702 | {file = "mypy_boto3_s3-1.40.0-py3-none-any.whl", hash = "sha256:5736b7780d57a156312d8d136462c207671d0236b0355704b5754496bb712bc8"}, 703 | {file = "mypy_boto3_s3-1.40.0.tar.gz", hash = "sha256:99a4a27f04d62fe0b31032f274f2e19889fa66424413617a9416873c48567f1d"}, 704 | ] 705 | 706 | [package.dependencies] 707 | typing-extensions = {version = "*", markers = "python_version < \"3.12\""} 708 | 709 | [[package]] 710 | name = "mypy-boto3-sqs" 711 | version = "1.40.0" 712 | description = "Type annotations for boto3 SQS 1.40.0 service generated with mypy-boto3-builder 8.11.0" 713 | optional = false 714 | python-versions = ">=3.8" 715 | files = [ 716 | {file = "mypy_boto3_sqs-1.40.0-py3-none-any.whl", hash = "sha256:af9055ccf1612bc53b7849beb761b751f5a7c94ee7562c03ebb16a3583945a40"}, 717 | {file = "mypy_boto3_sqs-1.40.0.tar.gz", hash = "sha256:03d0b5b488e3d01f2419400ba245dd7b89bbe06a438a5d4f59d358eeead19bb4"}, 718 | ] 719 | 720 | [package.dependencies] 721 | typing-extensions = {version = "*", markers = "python_version < \"3.12\""} 722 | 723 | [[package]] 724 | name = "mypy-extensions" 725 | version = "1.0.0" 726 | description = "Type system extensions for programs checked with the mypy type checker." 727 | optional = false 728 | python-versions = ">=3.5" 729 | files = [ 730 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 731 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 732 | ] 733 | 734 | [[package]] 735 | name = "packaging" 736 | version = "24.2" 737 | description = "Core utilities for Python packages" 738 | optional = false 739 | python-versions = ">=3.8" 740 | files = [ 741 | {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, 742 | {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, 743 | ] 744 | 745 | [[package]] 746 | name = "pastel" 747 | version = "0.2.1" 748 | description = "Bring colors to your terminal." 749 | optional = false 750 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 751 | files = [ 752 | {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, 753 | {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, 754 | ] 755 | 756 | [[package]] 757 | name = "pathspec" 758 | version = "0.12.1" 759 | description = "Utility library for gitignore style pattern matching of file paths." 760 | optional = false 761 | python-versions = ">=3.8" 762 | files = [ 763 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 764 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 765 | ] 766 | 767 | [[package]] 768 | name = "pluggy" 769 | version = "1.5.0" 770 | description = "plugin and hook calling mechanisms for python" 771 | optional = false 772 | python-versions = ">=3.8" 773 | files = [ 774 | {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, 775 | {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, 776 | ] 777 | 778 | [package.extras] 779 | dev = ["pre-commit", "tox"] 780 | testing = ["pytest", "pytest-benchmark"] 781 | 782 | [[package]] 783 | name = "poethepoet" 784 | version = "0.37.0" 785 | description = "A task runner that works well with poetry and uv." 786 | optional = false 787 | python-versions = ">=3.9" 788 | files = [ 789 | {file = "poethepoet-0.37.0-py3-none-any.whl", hash = "sha256:861790276315abcc8df1b4bd60e28c3d48a06db273edd3092f3c94e1a46e5e22"}, 790 | {file = "poethepoet-0.37.0.tar.gz", hash = "sha256:73edf458707c674a079baa46802e21455bda3a7f82a408e58c31b9f4fe8e933d"}, 791 | ] 792 | 793 | [package.dependencies] 794 | pastel = ">=0.2.1,<0.3.0" 795 | pyyaml = ">=6.0.2,<7.0" 796 | tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} 797 | 798 | [package.extras] 799 | poetry-plugin = ["poetry (>=1.2.0,<3.0.0)"] 800 | 801 | [[package]] 802 | name = "pydantic" 803 | version = "2.12.4" 804 | description = "Data validation using Python type hints" 805 | optional = false 806 | python-versions = ">=3.9" 807 | files = [ 808 | {file = "pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e"}, 809 | {file = "pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac"}, 810 | ] 811 | 812 | [package.dependencies] 813 | annotated-types = ">=0.6.0" 814 | pydantic-core = "2.41.5" 815 | typing-extensions = ">=4.14.1" 816 | typing-inspection = ">=0.4.2" 817 | 818 | [package.extras] 819 | email = ["email-validator (>=2.0.0)"] 820 | timezone = ["tzdata"] 821 | 822 | [[package]] 823 | name = "pydantic-core" 824 | version = "2.41.5" 825 | description = "Core functionality for Pydantic validation and serialization" 826 | optional = false 827 | python-versions = ">=3.9" 828 | files = [ 829 | {file = "pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146"}, 830 | {file = "pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2"}, 831 | {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97"}, 832 | {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9"}, 833 | {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52"}, 834 | {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941"}, 835 | {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a"}, 836 | {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c"}, 837 | {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2"}, 838 | {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556"}, 839 | {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49"}, 840 | {file = "pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba"}, 841 | {file = "pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9"}, 842 | {file = "pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6"}, 843 | {file = "pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b"}, 844 | {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a"}, 845 | {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8"}, 846 | {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e"}, 847 | {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1"}, 848 | {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b"}, 849 | {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b"}, 850 | {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284"}, 851 | {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594"}, 852 | {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e"}, 853 | {file = "pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b"}, 854 | {file = "pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe"}, 855 | {file = "pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f"}, 856 | {file = "pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7"}, 857 | {file = "pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0"}, 858 | {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69"}, 859 | {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75"}, 860 | {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05"}, 861 | {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc"}, 862 | {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c"}, 863 | {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5"}, 864 | {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c"}, 865 | {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294"}, 866 | {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1"}, 867 | {file = "pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d"}, 868 | {file = "pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815"}, 869 | {file = "pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3"}, 870 | {file = "pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9"}, 871 | {file = "pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34"}, 872 | {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0"}, 873 | {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33"}, 874 | {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e"}, 875 | {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2"}, 876 | {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586"}, 877 | {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d"}, 878 | {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740"}, 879 | {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e"}, 880 | {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858"}, 881 | {file = "pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36"}, 882 | {file = "pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11"}, 883 | {file = "pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd"}, 884 | {file = "pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a"}, 885 | {file = "pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14"}, 886 | {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1"}, 887 | {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66"}, 888 | {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869"}, 889 | {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2"}, 890 | {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375"}, 891 | {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553"}, 892 | {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90"}, 893 | {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07"}, 894 | {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb"}, 895 | {file = "pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23"}, 896 | {file = "pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf"}, 897 | {file = "pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0"}, 898 | {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a"}, 899 | {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3"}, 900 | {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c"}, 901 | {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612"}, 902 | {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d"}, 903 | {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9"}, 904 | {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660"}, 905 | {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9"}, 906 | {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3"}, 907 | {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf"}, 908 | {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470"}, 909 | {file = "pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa"}, 910 | {file = "pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c"}, 911 | {file = "pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008"}, 912 | {file = "pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf"}, 913 | {file = "pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5"}, 914 | {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d"}, 915 | {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60"}, 916 | {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82"}, 917 | {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5"}, 918 | {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3"}, 919 | {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425"}, 920 | {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504"}, 921 | {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5"}, 922 | {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3"}, 923 | {file = "pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460"}, 924 | {file = "pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b"}, 925 | {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034"}, 926 | {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c"}, 927 | {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2"}, 928 | {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad"}, 929 | {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd"}, 930 | {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc"}, 931 | {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56"}, 932 | {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b"}, 933 | {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8"}, 934 | {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a"}, 935 | {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b"}, 936 | {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2"}, 937 | {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093"}, 938 | {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a"}, 939 | {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963"}, 940 | {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a"}, 941 | {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26"}, 942 | {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808"}, 943 | {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc"}, 944 | {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1"}, 945 | {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84"}, 946 | {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770"}, 947 | {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f"}, 948 | {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51"}, 949 | {file = "pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e"}, 950 | ] 951 | 952 | [package.dependencies] 953 | typing-extensions = ">=4.14.1" 954 | 955 | [[package]] 956 | name = "pygments" 957 | version = "2.19.1" 958 | description = "Pygments is a syntax highlighting package written in Python." 959 | optional = false 960 | python-versions = ">=3.8" 961 | files = [ 962 | {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, 963 | {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, 964 | ] 965 | 966 | [package.extras] 967 | windows-terminal = ["colorama (>=0.4.6)"] 968 | 969 | [[package]] 970 | name = "pytest" 971 | version = "8.4.2" 972 | description = "pytest: simple powerful testing with Python" 973 | optional = false 974 | python-versions = ">=3.9" 975 | files = [ 976 | {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, 977 | {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, 978 | ] 979 | 980 | [package.dependencies] 981 | colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} 982 | exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} 983 | iniconfig = ">=1" 984 | packaging = ">=20" 985 | pluggy = ">=1.5,<2" 986 | pygments = ">=2.7.2" 987 | tomli = {version = ">=1", markers = "python_version < \"3.11\""} 988 | 989 | [package.extras] 990 | dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] 991 | 992 | [[package]] 993 | name = "python-dateutil" 994 | version = "2.9.0.post0" 995 | description = "Extensions to the standard Python datetime module" 996 | optional = false 997 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 998 | files = [ 999 | {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, 1000 | {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, 1001 | ] 1002 | 1003 | [package.dependencies] 1004 | six = ">=1.5" 1005 | 1006 | [[package]] 1007 | name = "pyyaml" 1008 | version = "6.0.3" 1009 | description = "YAML parser and emitter for Python" 1010 | optional = false 1011 | python-versions = ">=3.8" 1012 | files = [ 1013 | {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, 1014 | {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, 1015 | {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, 1016 | {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, 1017 | {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, 1018 | {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, 1019 | {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, 1020 | {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, 1021 | {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, 1022 | {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, 1023 | {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, 1024 | {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, 1025 | {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, 1026 | {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, 1027 | {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, 1028 | {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, 1029 | {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, 1030 | {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, 1031 | {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, 1032 | {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, 1033 | {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, 1034 | {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, 1035 | {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, 1036 | {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, 1037 | {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, 1038 | {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, 1039 | {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, 1040 | {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, 1041 | {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, 1042 | {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, 1043 | {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, 1044 | {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, 1045 | {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, 1046 | {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, 1047 | {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, 1048 | {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, 1049 | {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, 1050 | {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, 1051 | {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, 1052 | {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, 1053 | {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, 1054 | {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, 1055 | {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, 1056 | {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, 1057 | {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, 1058 | {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, 1059 | {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, 1060 | {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, 1061 | {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, 1062 | {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, 1063 | {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, 1064 | {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, 1065 | {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, 1066 | {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, 1067 | {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, 1068 | {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, 1069 | {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, 1070 | {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, 1071 | {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, 1072 | {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, 1073 | {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, 1074 | {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, 1075 | {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, 1076 | {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, 1077 | {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, 1078 | {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, 1079 | {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, 1080 | {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, 1081 | {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, 1082 | {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, 1083 | {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, 1084 | {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, 1085 | {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, 1086 | ] 1087 | 1088 | [[package]] 1089 | name = "ruff" 1090 | version = "0.14.4" 1091 | description = "An extremely fast Python linter and code formatter, written in Rust." 1092 | optional = false 1093 | python-versions = ">=3.7" 1094 | files = [ 1095 | {file = "ruff-0.14.4-py3-none-linux_armv6l.whl", hash = "sha256:e6604613ffbcf2297cd5dcba0e0ac9bd0c11dc026442dfbb614504e87c349518"}, 1096 | {file = "ruff-0.14.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d99c0b52b6f0598acede45ee78288e5e9b4409d1ce7f661f0fa36d4cbeadf9a4"}, 1097 | {file = "ruff-0.14.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9358d490ec030f1b51d048a7fd6ead418ed0826daf6149e95e30aa67c168af33"}, 1098 | {file = "ruff-0.14.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b40d27924f1f02dfa827b9c0712a13c0e4b108421665322218fc38caf615c2"}, 1099 | {file = "ruff-0.14.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f5e649052a294fe00818650712083cddc6cc02744afaf37202c65df9ea52efa5"}, 1100 | {file = "ruff-0.14.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa082a8f878deeba955531f975881828fd6afd90dfa757c2b0808aadb437136e"}, 1101 | {file = "ruff-0.14.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1043c6811c2419e39011890f14d0a30470f19d47d197c4858b2787dfa698f6c8"}, 1102 | {file = "ruff-0.14.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9f3a936ac27fb7c2a93e4f4b943a662775879ac579a433291a6f69428722649"}, 1103 | {file = "ruff-0.14.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95643ffd209ce78bc113266b88fba3d39e0461f0cbc8b55fb92505030fb4a850"}, 1104 | {file = "ruff-0.14.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:456daa2fa1021bc86ca857f43fe29d5d8b3f0e55e9f90c58c317c1dcc2afc7b5"}, 1105 | {file = "ruff-0.14.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f911bba769e4a9f51af6e70037bb72b70b45a16db5ce73e1f72aefe6f6d62132"}, 1106 | {file = "ruff-0.14.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:76158a7369b3979fa878612c623a7e5430c18b2fd1c73b214945c2d06337db67"}, 1107 | {file = "ruff-0.14.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f3b8f3b442d2b14c246e7aeca2e75915159e06a3540e2f4bed9f50d062d24469"}, 1108 | {file = "ruff-0.14.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c62da9a06779deecf4d17ed04939ae8b31b517643b26370c3be1d26f3ef7dbde"}, 1109 | {file = "ruff-0.14.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5a443a83a1506c684e98acb8cb55abaf3ef725078be40237463dae4463366349"}, 1110 | {file = "ruff-0.14.4-py3-none-win32.whl", hash = "sha256:643b69cb63cd996f1fc7229da726d07ac307eae442dd8974dbc7cf22c1e18fff"}, 1111 | {file = "ruff-0.14.4-py3-none-win_amd64.whl", hash = "sha256:26673da283b96fe35fa0c939bf8411abec47111644aa9f7cfbd3c573fb125d2c"}, 1112 | {file = "ruff-0.14.4-py3-none-win_arm64.whl", hash = "sha256:dd09c292479596b0e6fec8cd95c65c3a6dc68e9ad17b8f2382130f87ff6a75bb"}, 1113 | {file = "ruff-0.14.4.tar.gz", hash = "sha256:f459a49fe1085a749f15414ca76f61595f1a2cc8778ed7c279b6ca2e1fd19df3"}, 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "s3transfer" 1118 | version = "0.14.0" 1119 | description = "An Amazon S3 Transfer Manager" 1120 | optional = false 1121 | python-versions = ">=3.9" 1122 | files = [ 1123 | {file = "s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456"}, 1124 | {file = "s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125"}, 1125 | ] 1126 | 1127 | [package.dependencies] 1128 | botocore = ">=1.37.4,<2.0a.0" 1129 | 1130 | [package.extras] 1131 | crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"] 1132 | 1133 | [[package]] 1134 | name = "six" 1135 | version = "1.16.0" 1136 | description = "Python 2 and 3 compatibility utilities" 1137 | optional = false 1138 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 1139 | files = [ 1140 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1141 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1142 | ] 1143 | 1144 | [[package]] 1145 | name = "tomli" 1146 | version = "2.1.0" 1147 | description = "A lil' TOML parser" 1148 | optional = false 1149 | python-versions = ">=3.8" 1150 | files = [ 1151 | {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, 1152 | {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, 1153 | ] 1154 | 1155 | [[package]] 1156 | name = "types-awscrt" 1157 | version = "0.23.0" 1158 | description = "Type annotations and code completion for awscrt" 1159 | optional = false 1160 | python-versions = ">=3.8" 1161 | files = [ 1162 | {file = "types_awscrt-0.23.0-py3-none-any.whl", hash = "sha256:517d9d06f19cf58d778ca90ad01e52e0489466bf70dcf78c7f47f74fdf151a60"}, 1163 | {file = "types_awscrt-0.23.0.tar.gz", hash = "sha256:3fd1edeac923d1956c0e907c973fb83bda465beae7f054716b371b293f9b5fdc"}, 1164 | ] 1165 | 1166 | [[package]] 1167 | name = "types-pyyaml" 1168 | version = "6.0.12.20240917" 1169 | description = "Typing stubs for PyYAML" 1170 | optional = false 1171 | python-versions = ">=3.8" 1172 | files = [ 1173 | {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, 1174 | {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, 1175 | ] 1176 | 1177 | [[package]] 1178 | name = "types-s3transfer" 1179 | version = "0.10.4" 1180 | description = "Type annotations and code completion for s3transfer" 1181 | optional = false 1182 | python-versions = ">=3.8" 1183 | files = [ 1184 | {file = "types_s3transfer-0.10.4-py3-none-any.whl", hash = "sha256:22ac1aabc98f9d7f2928eb3fb4d5c02bf7435687f0913345a97dd3b84d0c217d"}, 1185 | {file = "types_s3transfer-0.10.4.tar.gz", hash = "sha256:03123477e3064c81efe712bf9d372c7c72f2790711431f9baa59cf96ea607267"}, 1186 | ] 1187 | 1188 | [[package]] 1189 | name = "typing-extensions" 1190 | version = "4.15.0" 1191 | description = "Backported and Experimental Type Hints for Python 3.9+" 1192 | optional = false 1193 | python-versions = ">=3.9" 1194 | files = [ 1195 | {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, 1196 | {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, 1197 | ] 1198 | 1199 | [[package]] 1200 | name = "typing-inspection" 1201 | version = "0.4.2" 1202 | description = "Runtime typing introspection tools" 1203 | optional = false 1204 | python-versions = ">=3.9" 1205 | files = [ 1206 | {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, 1207 | {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, 1208 | ] 1209 | 1210 | [package.dependencies] 1211 | typing-extensions = ">=4.12.0" 1212 | 1213 | [[package]] 1214 | name = "urllib3" 1215 | version = "2.2.3" 1216 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1217 | optional = false 1218 | python-versions = ">=3.8" 1219 | files = [ 1220 | {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, 1221 | {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, 1222 | ] 1223 | 1224 | [package.extras] 1225 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 1226 | h2 = ["h2 (>=4,<5)"] 1227 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 1228 | zstd = ["zstandard (>=0.18.0)"] 1229 | 1230 | [metadata] 1231 | lock-version = "2.0" 1232 | python-versions = "^3.10" 1233 | content-hash = "b095eddf94863b1539632432b7b1c24f10c29874ccb4d22575728e06de503997" 1234 | --------------------------------------------------------------------------------