From ce851e9dd7bf6147992c47973d6fdbb90ee856a4 Mon Sep 17 00:00:00 2001 From: Ran Isenberg Date: Wed, 6 Apr 2022 20:26:07 +0300 Subject: [PATCH 01/18] feat(parameters): Allow settings boto3.client() arguments --- .../utilities/parameters/appconfig.py | 13 ++- .../utilities/parameters/secrets.py | 18 +++- .../utilities/parameters/ssm.py | 18 +++- docs/utilities/parameters.md | 88 +++++++++++-------- 4 files changed, 90 insertions(+), 47 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index 3455617e952..225695ff7f0 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -30,7 +30,9 @@ class AppConfigProvider(BaseProvider): config: botocore.config.Config, optional Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional - Boto3 session to use for AWS API communication + Boto3 session to use for AWS API communication, will not be used if boto3_client is not None + boto3_client: boto3.client, optional + Boto3 Client to use for AWS API communication, will be used instead of boto3_session if both provided Example ------- @@ -68,14 +70,19 @@ def __init__( application: Optional[str] = None, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None, + boto3_client: Optional[boto3.client] = None, ): """ Initialize the App Config client """ config = config or Config() - session = boto3_session or boto3.session.Session() - self.client = session.client("appconfig", config=config) + if boto3_client is not None: + self.client = boto3_client + else: + session = boto3_session or boto3.session.Session() + self.client = session.client("appconfig", config=config) + self.application = resolve_env_var_choice( choice=application, env=os.getenv(constants.SERVICE_NAME_ENV, "service_undefined") ) diff --git a/aws_lambda_powertools/utilities/parameters/secrets.py b/aws_lambda_powertools/utilities/parameters/secrets.py index b64e70ae184..1f70a979301 100644 --- a/aws_lambda_powertools/utilities/parameters/secrets.py +++ b/aws_lambda_powertools/utilities/parameters/secrets.py @@ -20,7 +20,9 @@ class SecretsProvider(BaseProvider): config: botocore.config.Config, optional Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional - Boto3 session to use for AWS API communication + Boto3 session to use for AWS API communication, will not be used if boto3_client is not None + boto3_client: boto3.client, optional + Boto3 Client to use for AWS API communication, will be used instead of boto3_session if both provided Example ------- @@ -60,14 +62,22 @@ class SecretsProvider(BaseProvider): client: Any = None - def __init__(self, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None): + def __init__( + self, + config: Optional[Config] = None, + boto3_session: Optional[boto3.session.Session] = None, + boto3_client: Optional[boto3.client] = None, + ): """ Initialize the Secrets Manager client """ config = config or Config() - session = boto3_session or boto3.session.Session() - self.client = session.client("secretsmanager", config=config) + if boto3_client is not None: + self.client = boto3_client + else: + session = boto3_session or boto3.session.Session() + self.client = session.client("secretsmanager", config=config) super().__init__() diff --git a/aws_lambda_powertools/utilities/parameters/ssm.py b/aws_lambda_powertools/utilities/parameters/ssm.py index fd55e40a95f..6abe5193153 100644 --- a/aws_lambda_powertools/utilities/parameters/ssm.py +++ b/aws_lambda_powertools/utilities/parameters/ssm.py @@ -20,7 +20,9 @@ class SSMProvider(BaseProvider): config: botocore.config.Config, optional Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional - Boto3 session to use for AWS API communication + Boto3 session to use for AWS API communication, will not be used if boto3_client is not None + boto3_client: boto3.client, optional + Boto3 Client to use for AWS API communication, will be used instead of boto3_session if both provided Example ------- @@ -76,14 +78,22 @@ class SSMProvider(BaseProvider): client: Any = None - def __init__(self, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None): + def __init__( + self, + config: Optional[Config] = None, + boto3_session: Optional[boto3.session.Session] = None, + boto3_client: Optional[boto3.client] = None, + ): """ Initialize the SSM Parameter Store client """ config = config or Config() - session = boto3_session or boto3.session.Session() - self.client = session.client("ssm", config=config) + if boto3_client is not None: + self.client = boto3_client + else: + session = boto3_session or boto3.session.Session() + self.client = session.client("ssm", config=config) super().__init__() diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index d02a3feb73a..01b23171b17 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -24,14 +24,14 @@ This utility requires additional permissions to work as expected. ???+ note Different parameter providers require different permissions. -Provider | Function/Method | IAM Permission -------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------- -SSM Parameter Store | `get_parameter`, `SSMProvider.get` | `ssm:GetParameter` -SSM Parameter Store | `get_parameters`, `SSMProvider.get_multiple` | `ssm:GetParametersByPath` -Secrets Manager | `get_secret`, `SecretsManager.get` | `secretsmanager:GetSecretValue` -DynamoDB | `DynamoDBProvider.get` | `dynamodb:GetItem` -DynamoDB | `DynamoDBProvider.get_multiple` | `dynamodb:Query` -App Config | `AppConfigProvider.get_app_config`, `get_app_config` | `appconfig:GetConfiguration` +| Provider | Function/Method | IAM Permission | +| ------------------- | ---------------------------------------------------- | ------------------------------- | +| SSM Parameter Store | `get_parameter`, `SSMProvider.get` | `ssm:GetParameter` | +| SSM Parameter Store | `get_parameters`, `SSMProvider.get_multiple` | `ssm:GetParametersByPath` | +| Secrets Manager | `get_secret`, `SecretsManager.get` | `secretsmanager:GetSecretValue` | +| DynamoDB | `DynamoDBProvider.get` | `dynamodb:GetItem` | +| DynamoDB | `DynamoDBProvider.get_multiple` | `dynamodb:Query` | +| App Config | `AppConfigProvider.get_app_config`, `get_app_config` | `appconfig:GetConfiguration` | ### Fetching parameters @@ -147,10 +147,10 @@ def handler(event, context): The AWS Systems Manager Parameter Store provider supports two additional arguments for the `get()` and `get_multiple()` methods: -| Parameter | Default | Description | -|---------------|---------|-------------| -| **decrypt** | `False` | Will automatically decrypt the parameter. -| **recursive** | `True` | For `get_multiple()` only, will fetch all parameter values recursively based on a path prefix. +| Parameter | Default | Description | +| ------------- | ------- | ---------------------------------------------------------------------------------------------- | +| **decrypt** | `False` | Will automatically decrypt the parameter. | +| **recursive** | `True` | For `get_multiple()` only, will fetch all parameter values recursively based on a path prefix. | ```python hl_lines="6 8" title="Example with get() and get_multiple()" from aws_lambda_powertools.utilities import parameters @@ -189,9 +189,9 @@ For single parameters, you must use `id` as the [partition key](https://docs.aws DynamoDB table with `id` partition key and `value` as attribute - | id | value | - |--------------|----------| - | my-parameter | my-value | + | id | value | + | ------------ | -------- | + | my-parameter | my-value | With this table, `dynamodb_provider.get("my-param")` will return `my-value`. @@ -223,11 +223,11 @@ You can retrieve multiple parameters sharing the same `id` by having a sort key DynamoDB table with `id` primary key, `sk` as sort key` and `value` as attribute - | id | sk | value | - |-------------|---------|------------| - | my-hash-key | param-a | my-value-a | - | my-hash-key | param-b | my-value-b | - | my-hash-key | param-c | my-value-c | + | id | sk | value | + | ----------- | ------- | ---------- | + | my-hash-key | param-a | my-value-a | + | my-hash-key | param-b | my-value-b | + | my-hash-key | param-c | my-value-c | With this table, `dynamodb_provider.get_multiple("my-hash-key")` will return a dictionary response in the shape of `sk:value`. @@ -261,12 +261,12 @@ With this table, `dynamodb_provider.get_multiple("my-hash-key")` will return a d DynamoDB provider can be customized at initialization to match your table structure: -| Parameter | Mandatory | Default | Description | -|----------------|-----------|---------|-------------| -| **table_name** | **Yes** | *(N/A)* | Name of the DynamoDB table containing the parameter values. -| **key_attr** | No | `id` | Hash key for the DynamoDB table. -| **sort_attr** | No | `sk` | Range key for the DynamoDB table. You don't need to set this if you don't use the `get_multiple()` method. -| **value_attr** | No | `value` | Name of the attribute containing the parameter value. +| Parameter | Mandatory | Default | Description | +| -------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------- | +| **table_name** | **Yes** | *(N/A)* | Name of the DynamoDB table containing the parameter values. | +| **key_attr** | No | `id` | Hash key for the DynamoDB table. | +| **sort_attr** | No | `sk` | Range key for the DynamoDB table. You don't need to set this if you don't use the `get_multiple()` method. | +| **value_attr** | No | `value` | Name of the attribute containing the parameter value. | ```python hl_lines="3-8" title="Customizing DynamoDBProvider to suit your table design" from aws_lambda_powertools.utilities import parameters @@ -467,22 +467,22 @@ def handler(event, context): Here is the mapping between this utility's functions and methods and the underlying SDK: -| Provider | Function/Method | Client name | Function name | -|---------------------|---------------------------------|------------------|----------------| -| SSM Parameter Store | `get_parameter` | `ssm` | [get_parameter](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.get_parameter) | -| SSM Parameter Store | `get_parameters` | `ssm` | [get_parameters_by_path](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.get_parameters_by_path) | -| SSM Parameter Store | `SSMProvider.get` | `ssm` | [get_parameter](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.get_parameter) | -| SSM Parameter Store | `SSMProvider.get_multiple` | `ssm` | [get_parameters_by_path](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.get_parameters_by_path) | +| Provider | Function/Method | Client name | Function name | +| ------------------- | ------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SSM Parameter Store | `get_parameter` | `ssm` | [get_parameter](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.get_parameter) | +| SSM Parameter Store | `get_parameters` | `ssm` | [get_parameters_by_path](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.get_parameters_by_path) | +| SSM Parameter Store | `SSMProvider.get` | `ssm` | [get_parameter](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.get_parameter) | +| SSM Parameter Store | `SSMProvider.get_multiple` | `ssm` | [get_parameters_by_path](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.get_parameters_by_path) | | Secrets Manager | `get_secret` | `secretsmanager` | [get_secret_value](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.get_secret_value) | | Secrets Manager | `SecretsManager.get` | `secretsmanager` | [get_secret_value](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.get_secret_value) | -| DynamoDB | `DynamoDBProvider.get` | `dynamodb` | ([Table resource](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#table)) | [get_item](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Table.get_item) -| DynamoDB | `DynamoDBProvider.get_multiple` | `dynamodb` | ([Table resource](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#table)) | [query](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Table.query) -| App Config | `get_app_config` | `appconfig` | [get_configuration](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appconfig.html#AppConfig.Client.get_configuration) | +| DynamoDB | `DynamoDBProvider.get` | `dynamodb` | ([Table resource](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#table)) | [get_item](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Table.get_item) | +| DynamoDB | `DynamoDBProvider.get_multiple` | `dynamodb` | ([Table resource](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#table)) | [query](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Table.query) | +| App Config | `get_app_config` | `appconfig` | [get_configuration](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appconfig.html#AppConfig.Client.get_configuration) | ### Customizing boto configuration -The **`config`** and **`boto3_session`** parameters enable you to pass in a custom [botocore config object](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html) or a custom [boto3 session](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html) when constructing any of the built-in provider classes. +The **`config`** and **`boto3_session`** and **`boto3_client`** parameters enable you to pass in a custom [botocore config object](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html) or a custom [boto3 session](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html) or a custom [boto3 client](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/boto3.html) when constructing any of the built-in provider classes. ???+ tip You can use a custom session for retrieving parameters cross-account/region and for snapshot testing. @@ -516,6 +516,22 @@ The **`config`** and **`boto3_session`** parameters enable you to pass in a cust ... ``` +=== "Custom client" + + ```python hl_lines="2 4 5" + from aws_lambda_powertools.utilities import parameters + import boto3 + + boto3_client= boto3.client("ssm") + ssm_provider = parameters.SSMProvider(boto3_client=boto3_client) + + def handler(event, context): + # Retrieve a single parameter + value = ssm_provider.get("/my/parameter") + ... + ``` + + ## Testing your code For unit testing your applications, you can mock the calls to the parameters utility to avoid calling AWS APIs. This From b9b6753b2dc0ccac11398d0fc61450079df48e1e Mon Sep 17 00:00:00 2001 From: Ran Isenberg Date: Wed, 6 Apr 2022 20:50:32 +0300 Subject: [PATCH 02/18] added tests --- tests/functional/test_utilities_parameters.py | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/tests/functional/test_utilities_parameters.py b/tests/functional/test_utilities_parameters.py index 47fc5a0e982..cf3cce9b233 100644 --- a/tests/functional/test_utilities_parameters.py +++ b/tests/functional/test_utilities_parameters.py @@ -6,6 +6,7 @@ from io import BytesIO from typing import Dict +import boto3 import pytest from boto3.dynamodb.conditions import Key from botocore import stub @@ -444,6 +445,43 @@ def test_ssm_provider_get(mock_name, mock_value, mock_version, config): stubber.deactivate() +def test_ssm_provider_get_custom_client(mock_name, mock_value, mock_version): + """ + Test SSMProvider.get() with a non-cached value + """ + + client = boto3.client("ssm") + + # Create a new provider + provider = parameters.SSMProvider(boto3_client=client) + + # Stub the boto3 client + stubber = stub.Stubber(provider.client) + response = { + "Parameter": { + "Name": mock_name, + "Type": "String", + "Value": mock_value, + "Version": mock_version, + "Selector": f"{mock_name}:{mock_version}", + "SourceResult": "string", + "LastModifiedDate": datetime(2015, 1, 1), + "ARN": f"arn:aws:ssm:us-east-2:111122223333:parameter/{mock_name}", + } + } + expected_params = {"Name": mock_name, "WithDecryption": False} + stubber.add_response("get_parameter", response, expected_params) + stubber.activate() + + try: + value = provider.get(mock_name) + + assert value == mock_value + stubber.assert_no_pending_responses() + finally: + stubber.deactivate() + + def test_ssm_provider_get_default_config(monkeypatch, mock_name, mock_value, mock_version): """ Test SSMProvider.get() without specifying the config @@ -852,6 +890,37 @@ def test_secrets_provider_get(mock_name, mock_value, config): stubber.deactivate() +def test_secrets_provider_get_with_custom_client(mock_name, mock_value): + """ + Test SecretsProvider.get() with a non-cached value + """ + client = boto3.client("secretsmanager") + + # Create a new provider + provider = parameters.SecretsProvider(boto3_client=client) + + # Stub the boto3 client + stubber = stub.Stubber(provider.client) + response = { + "ARN": f"arn:aws:secretsmanager:us-east-1:132456789012:secret/{mock_name}", + "Name": mock_name, + "VersionId": "7a9155b8-2dc9-466e-b4f6-5bc46516c84d", + "SecretString": mock_value, + "CreatedDate": datetime(2015, 1, 1), + } + expected_params = {"SecretId": mock_name} + stubber.add_response("get_secret_value", response, expected_params) + stubber.activate() + + try: + value = provider.get(mock_name) + + assert value == mock_value + stubber.assert_no_pending_responses() + finally: + stubber.deactivate() + + def test_secrets_provider_get_default_config(monkeypatch, mock_name, mock_value): """ Test SecretsProvider.get() without specifying a config @@ -1482,6 +1551,37 @@ def test_appconf_provider_get_configuration_json_content_type(mock_name, config) stubber.deactivate() +def test_appconf_provider_get_configuration_json_content_type_with_custom_client(mock_name): + """ + Test get_configuration.get with default values + """ + + client = boto3.client("appconfig") + + # Create a new provider + environment = "dev" + application = "myapp" + provider = parameters.AppConfigProvider(environment=environment, application=application, boto3_client=client) + + mock_body_json = {"myenvvar1": "Black Panther", "myenvvar2": 3} + encoded_message = json.dumps(mock_body_json).encode("utf-8") + mock_value = StreamingBody(BytesIO(encoded_message), len(encoded_message)) + + # Stub the boto3 client + stubber = stub.Stubber(provider.client) + response = {"Content": mock_value, "ConfigurationVersion": "1", "ContentType": "application/json"} + stubber.add_response("get_configuration", response) + stubber.activate() + + try: + value = provider.get(mock_name, transform="json", ClientConfigurationVersion="2") + + assert value == mock_body_json + stubber.assert_no_pending_responses() + finally: + stubber.deactivate() + + def test_appconf_provider_get_configuration_no_transform(mock_name, config): """ Test appconfigprovider.get with default values From 159ca2258f68c584202f9e42e053f8fbab752c75 Mon Sep 17 00:00:00 2001 From: Ran Isenberg Date: Wed, 6 Apr 2022 21:10:57 +0300 Subject: [PATCH 03/18] added tests --- tests/functional/test_utilities_parameters.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/test_utilities_parameters.py b/tests/functional/test_utilities_parameters.py index cf3cce9b233..fd6a2ddcc23 100644 --- a/tests/functional/test_utilities_parameters.py +++ b/tests/functional/test_utilities_parameters.py @@ -445,12 +445,12 @@ def test_ssm_provider_get(mock_name, mock_value, mock_version, config): stubber.deactivate() -def test_ssm_provider_get_custom_client(mock_name, mock_value, mock_version): +def test_ssm_provider_get_with_custom_client(mock_name, mock_value, mock_version, config): """ Test SSMProvider.get() with a non-cached value """ - client = boto3.client("ssm") + client = boto3.client("ssm", config=config) # Create a new provider provider = parameters.SSMProvider(boto3_client=client) @@ -890,11 +890,11 @@ def test_secrets_provider_get(mock_name, mock_value, config): stubber.deactivate() -def test_secrets_provider_get_with_custom_client(mock_name, mock_value): +def test_secrets_provider_get_with_custom_client(mock_name, mock_value, config): """ Test SecretsProvider.get() with a non-cached value """ - client = boto3.client("secretsmanager") + client = boto3.client("secretsmanager", config=config) # Create a new provider provider = parameters.SecretsProvider(boto3_client=client) @@ -1551,12 +1551,12 @@ def test_appconf_provider_get_configuration_json_content_type(mock_name, config) stubber.deactivate() -def test_appconf_provider_get_configuration_json_content_type_with_custom_client(mock_name): +def test_appconf_provider_get_configuration_json_content_type_with_custom_client(mock_name, config): """ Test get_configuration.get with default values """ - client = boto3.client("appconfig") + client = boto3.client("appconfig", config=config) # Create a new provider environment = "dev" From d33201e1de02e8b0f8db163481b4f1bda63f539c Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Fri, 8 Apr 2022 10:59:08 +0200 Subject: [PATCH 04/18] chore: paying older debts on grammar --- docs/utilities/parameters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 01b23171b17..bd517f4b583 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -482,7 +482,7 @@ Here is the mapping between this utility's functions and methods and the underly ### Customizing boto configuration -The **`config`** and **`boto3_session`** and **`boto3_client`** parameters enable you to pass in a custom [botocore config object](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html) or a custom [boto3 session](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html) or a custom [boto3 client](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/boto3.html) when constructing any of the built-in provider classes. +The **`config`** , **`boto3_session`**, and **`boto3_client`** parameters enable you to pass in a custom [botocore config object](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html) , [boto3 session](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html), or a [boto3 client](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/boto3.html) when constructing any of the built-in provider classes. ???+ tip You can use a custom session for retrieving parameters cross-account/region and for snapshot testing. From 2fbdca343cb1a51ee2a5b90d3267961770972140 Mon Sep 17 00:00:00 2001 From: Ran Isenberg Date: Sun, 10 Apr 2022 09:20:51 +0300 Subject: [PATCH 05/18] added mypy type hints --- .../utilities/parameters/appconfig.py | 7 +- .../utilities/parameters/secrets.py | 7 +- .../utilities/parameters/ssm.py | 7 +- poetry.lock | 429 ++++++++++++++++-- pyproject.toml | 1 + 5 files changed, 403 insertions(+), 48 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index 225695ff7f0..ebba175f601 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -9,6 +9,7 @@ import boto3 from botocore.config import Config +from mypy_boto3_appconfig import AppConfigClient from ...shared import constants from ...shared.functions import resolve_env_var_choice @@ -31,7 +32,7 @@ class AppConfigProvider(BaseProvider): Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional Boto3 session to use for AWS API communication, will not be used if boto3_client is not None - boto3_client: boto3.client, optional + boto3_client: AppConfigClient, optional Boto3 Client to use for AWS API communication, will be used instead of boto3_session if both provided Example @@ -70,7 +71,7 @@ def __init__( application: Optional[str] = None, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None, - boto3_client: Optional[boto3.client] = None, + boto3_client: Optional[AppConfigClient] = None, ): """ Initialize the App Config client @@ -81,7 +82,7 @@ def __init__( self.client = boto3_client else: session = boto3_session or boto3.session.Session() - self.client = session.client("appconfig", config=config) + self.client: AppConfigClient = session.client("appconfig", config=config) self.application = resolve_env_var_choice( choice=application, env=os.getenv(constants.SERVICE_NAME_ENV, "service_undefined") diff --git a/aws_lambda_powertools/utilities/parameters/secrets.py b/aws_lambda_powertools/utilities/parameters/secrets.py index 1f70a979301..5bb6fe34027 100644 --- a/aws_lambda_powertools/utilities/parameters/secrets.py +++ b/aws_lambda_powertools/utilities/parameters/secrets.py @@ -7,6 +7,7 @@ import boto3 from botocore.config import Config +from mypy_boto3_secretsmanager import SecretsManagerClient from .base import DEFAULT_MAX_AGE_SECS, DEFAULT_PROVIDERS, BaseProvider @@ -21,7 +22,7 @@ class SecretsProvider(BaseProvider): Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional Boto3 session to use for AWS API communication, will not be used if boto3_client is not None - boto3_client: boto3.client, optional + boto3_client: SecretsManagerClient, optional Boto3 Client to use for AWS API communication, will be used instead of boto3_session if both provided Example @@ -66,7 +67,7 @@ def __init__( self, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None, - boto3_client: Optional[boto3.client] = None, + boto3_client: Optional[SecretsManagerClient] = None, ): """ Initialize the Secrets Manager client @@ -77,7 +78,7 @@ def __init__( self.client = boto3_client else: session = boto3_session or boto3.session.Session() - self.client = session.client("secretsmanager", config=config) + self.client: SecretsManagerClient = session.client("secretsmanager", config=config) super().__init__() diff --git a/aws_lambda_powertools/utilities/parameters/ssm.py b/aws_lambda_powertools/utilities/parameters/ssm.py index 6abe5193153..94ac31d5fe8 100644 --- a/aws_lambda_powertools/utilities/parameters/ssm.py +++ b/aws_lambda_powertools/utilities/parameters/ssm.py @@ -7,6 +7,7 @@ import boto3 from botocore.config import Config +from mypy_boto3_ssm import SSMClient from .base import DEFAULT_MAX_AGE_SECS, DEFAULT_PROVIDERS, BaseProvider @@ -21,7 +22,7 @@ class SSMProvider(BaseProvider): Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional Boto3 session to use for AWS API communication, will not be used if boto3_client is not None - boto3_client: boto3.client, optional + boto3_client: SSMClient, optional Boto3 Client to use for AWS API communication, will be used instead of boto3_session if both provided Example @@ -82,7 +83,7 @@ def __init__( self, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None, - boto3_client: Optional[boto3.client] = None, + boto3_client: Optional[SSMClient] = None, ): """ Initialize the SSM Parameter Store client @@ -93,7 +94,7 @@ def __init__( self.client = boto3_client else: session = boto3_session or boto3.session.Session() - self.client = session.client("ssm", config=config) + self.client: SSMClient = session.client("ssm", config=config) super().__init__() diff --git a/poetry.lock b/poetry.lock index f0ce338de99..4da392341b8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -91,6 +91,328 @@ s3transfer = ">=0.5.0,<0.6.0" [package.extras] crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] +[[package]] +name = "boto3-stubs" +version = "1.21.37" +description = "Type annotations for boto3 1.21.37 generated with mypy-boto3-builder 7.5.8" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +botocore-stubs = "*" +mypy-boto3-appconfig = {version = ">=1.21.0,<1.22.0", optional = true, markers = "extra == \"appconfig\""} +mypy-boto3-secretsmanager = {version = ">=1.21.0,<1.22.0", optional = true, markers = "extra == \"secretsmanager\""} +mypy-boto3-ssm = {version = ">=1.21.0,<1.22.0", optional = true, markers = "extra == \"ssm\""} +typing-extensions = ">=4.1.0" + +[package.extras] +accessanalyzer = ["mypy-boto3-accessanalyzer (>=1.21.0,<1.22.0)"] +account = ["mypy-boto3-account (>=1.21.0,<1.22.0)"] +acm = ["mypy-boto3-acm (>=1.21.0,<1.22.0)"] +acm-pca = ["mypy-boto3-acm-pca (>=1.21.0,<1.22.0)"] +alexaforbusiness = ["mypy-boto3-alexaforbusiness (>=1.21.0,<1.22.0)"] +all = ["mypy-boto3-accessanalyzer (>=1.21.0,<1.22.0)", "mypy-boto3-account (>=1.21.0,<1.22.0)", "mypy-boto3-acm (>=1.21.0,<1.22.0)", "mypy-boto3-acm-pca (>=1.21.0,<1.22.0)", "mypy-boto3-alexaforbusiness (>=1.21.0,<1.22.0)", "mypy-boto3-amp (>=1.21.0,<1.22.0)", "mypy-boto3-amplify (>=1.21.0,<1.22.0)", "mypy-boto3-amplifybackend (>=1.21.0,<1.22.0)", "mypy-boto3-amplifyuibuilder (>=1.21.0,<1.22.0)", "mypy-boto3-apigateway (>=1.21.0,<1.22.0)", "mypy-boto3-apigatewaymanagementapi (>=1.21.0,<1.22.0)", "mypy-boto3-apigatewayv2 (>=1.21.0,<1.22.0)", "mypy-boto3-appconfig (>=1.21.0,<1.22.0)", "mypy-boto3-appconfigdata (>=1.21.0,<1.22.0)", "mypy-boto3-appflow (>=1.21.0,<1.22.0)", "mypy-boto3-appintegrations (>=1.21.0,<1.22.0)", "mypy-boto3-application-autoscaling (>=1.21.0,<1.22.0)", "mypy-boto3-application-insights (>=1.21.0,<1.22.0)", "mypy-boto3-applicationcostprofiler (>=1.21.0,<1.22.0)", "mypy-boto3-appmesh (>=1.21.0,<1.22.0)", "mypy-boto3-apprunner (>=1.21.0,<1.22.0)", "mypy-boto3-appstream (>=1.21.0,<1.22.0)", "mypy-boto3-appsync (>=1.21.0,<1.22.0)", "mypy-boto3-athena (>=1.21.0,<1.22.0)", "mypy-boto3-auditmanager (>=1.21.0,<1.22.0)", "mypy-boto3-autoscaling (>=1.21.0,<1.22.0)", "mypy-boto3-autoscaling-plans (>=1.21.0,<1.22.0)", "mypy-boto3-backup (>=1.21.0,<1.22.0)", "mypy-boto3-backup-gateway (>=1.21.0,<1.22.0)", "mypy-boto3-batch (>=1.21.0,<1.22.0)", "mypy-boto3-billingconductor (>=1.21.0,<1.22.0)", "mypy-boto3-braket (>=1.21.0,<1.22.0)", "mypy-boto3-budgets (>=1.21.0,<1.22.0)", "mypy-boto3-ce (>=1.21.0,<1.22.0)", "mypy-boto3-chime (>=1.21.0,<1.22.0)", "mypy-boto3-chime-sdk-identity (>=1.21.0,<1.22.0)", "mypy-boto3-chime-sdk-meetings (>=1.21.0,<1.22.0)", "mypy-boto3-chime-sdk-messaging (>=1.21.0,<1.22.0)", "mypy-boto3-cloud9 (>=1.21.0,<1.22.0)", "mypy-boto3-cloudcontrol (>=1.21.0,<1.22.0)", "mypy-boto3-clouddirectory (>=1.21.0,<1.22.0)", "mypy-boto3-cloudformation (>=1.21.0,<1.22.0)", "mypy-boto3-cloudfront (>=1.21.0,<1.22.0)", "mypy-boto3-cloudhsm (>=1.21.0,<1.22.0)", "mypy-boto3-cloudhsmv2 (>=1.21.0,<1.22.0)", "mypy-boto3-cloudsearch (>=1.21.0,<1.22.0)", "mypy-boto3-cloudsearchdomain (>=1.21.0,<1.22.0)", "mypy-boto3-cloudtrail (>=1.21.0,<1.22.0)", "mypy-boto3-cloudwatch (>=1.21.0,<1.22.0)", "mypy-boto3-codeartifact (>=1.21.0,<1.22.0)", "mypy-boto3-codebuild (>=1.21.0,<1.22.0)", "mypy-boto3-codecommit (>=1.21.0,<1.22.0)", "mypy-boto3-codedeploy (>=1.21.0,<1.22.0)", "mypy-boto3-codeguru-reviewer (>=1.21.0,<1.22.0)", "mypy-boto3-codeguruprofiler (>=1.21.0,<1.22.0)", "mypy-boto3-codepipeline (>=1.21.0,<1.22.0)", "mypy-boto3-codestar (>=1.21.0,<1.22.0)", "mypy-boto3-codestar-connections (>=1.21.0,<1.22.0)", "mypy-boto3-codestar-notifications (>=1.21.0,<1.22.0)", "mypy-boto3-cognito-identity (>=1.21.0,<1.22.0)", "mypy-boto3-cognito-idp (>=1.21.0,<1.22.0)", "mypy-boto3-cognito-sync (>=1.21.0,<1.22.0)", "mypy-boto3-comprehend (>=1.21.0,<1.22.0)", "mypy-boto3-comprehendmedical (>=1.21.0,<1.22.0)", "mypy-boto3-compute-optimizer (>=1.21.0,<1.22.0)", "mypy-boto3-config (>=1.21.0,<1.22.0)", "mypy-boto3-connect (>=1.21.0,<1.22.0)", "mypy-boto3-connect-contact-lens (>=1.21.0,<1.22.0)", "mypy-boto3-connectparticipant (>=1.21.0,<1.22.0)", "mypy-boto3-cur (>=1.21.0,<1.22.0)", "mypy-boto3-customer-profiles (>=1.21.0,<1.22.0)", "mypy-boto3-databrew (>=1.21.0,<1.22.0)", "mypy-boto3-dataexchange (>=1.21.0,<1.22.0)", "mypy-boto3-datapipeline (>=1.21.0,<1.22.0)", "mypy-boto3-datasync (>=1.21.0,<1.22.0)", "mypy-boto3-dax (>=1.21.0,<1.22.0)", "mypy-boto3-detective (>=1.21.0,<1.22.0)", "mypy-boto3-devicefarm (>=1.21.0,<1.22.0)", "mypy-boto3-devops-guru (>=1.21.0,<1.22.0)", "mypy-boto3-directconnect (>=1.21.0,<1.22.0)", "mypy-boto3-discovery (>=1.21.0,<1.22.0)", "mypy-boto3-dlm (>=1.21.0,<1.22.0)", "mypy-boto3-dms (>=1.21.0,<1.22.0)", "mypy-boto3-docdb (>=1.21.0,<1.22.0)", "mypy-boto3-drs (>=1.21.0,<1.22.0)", "mypy-boto3-ds (>=1.21.0,<1.22.0)", "mypy-boto3-dynamodb (>=1.21.0,<1.22.0)", "mypy-boto3-dynamodbstreams (>=1.21.0,<1.22.0)", "mypy-boto3-ebs (>=1.21.0,<1.22.0)", "mypy-boto3-ec2 (>=1.21.0,<1.22.0)", "mypy-boto3-ec2-instance-connect (>=1.21.0,<1.22.0)", "mypy-boto3-ecr (>=1.21.0,<1.22.0)", "mypy-boto3-ecr-public (>=1.21.0,<1.22.0)", "mypy-boto3-ecs (>=1.21.0,<1.22.0)", "mypy-boto3-efs (>=1.21.0,<1.22.0)", "mypy-boto3-eks (>=1.21.0,<1.22.0)", "mypy-boto3-elastic-inference (>=1.21.0,<1.22.0)", "mypy-boto3-elasticache (>=1.21.0,<1.22.0)", "mypy-boto3-elasticbeanstalk (>=1.21.0,<1.22.0)", "mypy-boto3-elastictranscoder (>=1.21.0,<1.22.0)", "mypy-boto3-elb (>=1.21.0,<1.22.0)", "mypy-boto3-elbv2 (>=1.21.0,<1.22.0)", "mypy-boto3-emr (>=1.21.0,<1.22.0)", "mypy-boto3-emr-containers (>=1.21.0,<1.22.0)", "mypy-boto3-es (>=1.21.0,<1.22.0)", "mypy-boto3-events (>=1.21.0,<1.22.0)", "mypy-boto3-evidently (>=1.21.0,<1.22.0)", "mypy-boto3-finspace (>=1.21.0,<1.22.0)", "mypy-boto3-finspace-data (>=1.21.0,<1.22.0)", "mypy-boto3-firehose (>=1.21.0,<1.22.0)", "mypy-boto3-fis (>=1.21.0,<1.22.0)", "mypy-boto3-fms (>=1.21.0,<1.22.0)", "mypy-boto3-forecast (>=1.21.0,<1.22.0)", "mypy-boto3-forecastquery (>=1.21.0,<1.22.0)", "mypy-boto3-frauddetector (>=1.21.0,<1.22.0)", "mypy-boto3-fsx (>=1.21.0,<1.22.0)", "mypy-boto3-gamelift (>=1.21.0,<1.22.0)", "mypy-boto3-gamesparks (>=1.21.0,<1.22.0)", "mypy-boto3-glacier (>=1.21.0,<1.22.0)", "mypy-boto3-globalaccelerator (>=1.21.0,<1.22.0)", "mypy-boto3-glue (>=1.21.0,<1.22.0)", "mypy-boto3-grafana (>=1.21.0,<1.22.0)", "mypy-boto3-greengrass (>=1.21.0,<1.22.0)", "mypy-boto3-greengrassv2 (>=1.21.0,<1.22.0)", "mypy-boto3-groundstation (>=1.21.0,<1.22.0)", "mypy-boto3-guardduty (>=1.21.0,<1.22.0)", "mypy-boto3-health (>=1.21.0,<1.22.0)", "mypy-boto3-healthlake (>=1.21.0,<1.22.0)", "mypy-boto3-honeycode (>=1.21.0,<1.22.0)", "mypy-boto3-iam (>=1.21.0,<1.22.0)", "mypy-boto3-identitystore (>=1.21.0,<1.22.0)", "mypy-boto3-imagebuilder (>=1.21.0,<1.22.0)", "mypy-boto3-importexport (>=1.21.0,<1.22.0)", "mypy-boto3-inspector (>=1.21.0,<1.22.0)", "mypy-boto3-inspector2 (>=1.21.0,<1.22.0)", "mypy-boto3-iot (>=1.21.0,<1.22.0)", "mypy-boto3-iot-data (>=1.21.0,<1.22.0)", "mypy-boto3-iot-jobs-data (>=1.21.0,<1.22.0)", "mypy-boto3-iot1click-devices (>=1.21.0,<1.22.0)", "mypy-boto3-iot1click-projects (>=1.21.0,<1.22.0)", "mypy-boto3-iotanalytics (>=1.21.0,<1.22.0)", "mypy-boto3-iotdeviceadvisor (>=1.21.0,<1.22.0)", "mypy-boto3-iotevents (>=1.21.0,<1.22.0)", "mypy-boto3-iotevents-data (>=1.21.0,<1.22.0)", "mypy-boto3-iotfleethub (>=1.21.0,<1.22.0)", "mypy-boto3-iotsecuretunneling (>=1.21.0,<1.22.0)", "mypy-boto3-iotsitewise (>=1.21.0,<1.22.0)", "mypy-boto3-iotthingsgraph (>=1.21.0,<1.22.0)", "mypy-boto3-iottwinmaker (>=1.21.0,<1.22.0)", "mypy-boto3-iotwireless (>=1.21.0,<1.22.0)", "mypy-boto3-ivs (>=1.21.0,<1.22.0)", "mypy-boto3-kafka (>=1.21.0,<1.22.0)", "mypy-boto3-kafkaconnect (>=1.21.0,<1.22.0)", "mypy-boto3-kendra (>=1.21.0,<1.22.0)", "mypy-boto3-keyspaces (>=1.21.0,<1.22.0)", "mypy-boto3-kinesis (>=1.21.0,<1.22.0)", "mypy-boto3-kinesis-video-archived-media (>=1.21.0,<1.22.0)", "mypy-boto3-kinesis-video-media (>=1.21.0,<1.22.0)", "mypy-boto3-kinesis-video-signaling (>=1.21.0,<1.22.0)", "mypy-boto3-kinesisanalytics (>=1.21.0,<1.22.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.21.0,<1.22.0)", "mypy-boto3-kinesisvideo (>=1.21.0,<1.22.0)", "mypy-boto3-kms (>=1.21.0,<1.22.0)", "mypy-boto3-lakeformation (>=1.21.0,<1.22.0)", "mypy-boto3-lambda (>=1.21.0,<1.22.0)", "mypy-boto3-lex-models (>=1.21.0,<1.22.0)", "mypy-boto3-lex-runtime (>=1.21.0,<1.22.0)", "mypy-boto3-lexv2-models (>=1.21.0,<1.22.0)", "mypy-boto3-lexv2-runtime (>=1.21.0,<1.22.0)", "mypy-boto3-license-manager (>=1.21.0,<1.22.0)", "mypy-boto3-lightsail (>=1.21.0,<1.22.0)", "mypy-boto3-location (>=1.21.0,<1.22.0)", "mypy-boto3-logs (>=1.21.0,<1.22.0)", "mypy-boto3-lookoutequipment (>=1.21.0,<1.22.0)", "mypy-boto3-lookoutmetrics (>=1.21.0,<1.22.0)", "mypy-boto3-lookoutvision (>=1.21.0,<1.22.0)", "mypy-boto3-machinelearning (>=1.21.0,<1.22.0)", "mypy-boto3-macie (>=1.21.0,<1.22.0)", "mypy-boto3-macie2 (>=1.21.0,<1.22.0)", "mypy-boto3-managedblockchain (>=1.21.0,<1.22.0)", "mypy-boto3-marketplace-catalog (>=1.21.0,<1.22.0)", "mypy-boto3-marketplace-entitlement (>=1.21.0,<1.22.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.21.0,<1.22.0)", "mypy-boto3-mediaconnect (>=1.21.0,<1.22.0)", "mypy-boto3-mediaconvert (>=1.21.0,<1.22.0)", "mypy-boto3-medialive (>=1.21.0,<1.22.0)", "mypy-boto3-mediapackage (>=1.21.0,<1.22.0)", "mypy-boto3-mediapackage-vod (>=1.21.0,<1.22.0)", "mypy-boto3-mediastore (>=1.21.0,<1.22.0)", "mypy-boto3-mediastore-data (>=1.21.0,<1.22.0)", "mypy-boto3-mediatailor (>=1.21.0,<1.22.0)", "mypy-boto3-memorydb (>=1.21.0,<1.22.0)", "mypy-boto3-meteringmarketplace (>=1.21.0,<1.22.0)", "mypy-boto3-mgh (>=1.21.0,<1.22.0)", "mypy-boto3-mgn (>=1.21.0,<1.22.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.21.0,<1.22.0)", "mypy-boto3-migrationhub-config (>=1.21.0,<1.22.0)", "mypy-boto3-migrationhubstrategy (>=1.21.0,<1.22.0)", "mypy-boto3-mobile (>=1.21.0,<1.22.0)", "mypy-boto3-mq (>=1.21.0,<1.22.0)", "mypy-boto3-mturk (>=1.21.0,<1.22.0)", "mypy-boto3-mwaa (>=1.21.0,<1.22.0)", "mypy-boto3-neptune (>=1.21.0,<1.22.0)", "mypy-boto3-network-firewall (>=1.21.0,<1.22.0)", "mypy-boto3-networkmanager (>=1.21.0,<1.22.0)", "mypy-boto3-nimble (>=1.21.0,<1.22.0)", "mypy-boto3-opensearch (>=1.21.0,<1.22.0)", "mypy-boto3-opsworks (>=1.21.0,<1.22.0)", "mypy-boto3-opsworkscm (>=1.21.0,<1.22.0)", "mypy-boto3-organizations (>=1.21.0,<1.22.0)", "mypy-boto3-outposts (>=1.21.0,<1.22.0)", "mypy-boto3-panorama (>=1.21.0,<1.22.0)", "mypy-boto3-personalize (>=1.21.0,<1.22.0)", "mypy-boto3-personalize-events (>=1.21.0,<1.22.0)", "mypy-boto3-personalize-runtime (>=1.21.0,<1.22.0)", "mypy-boto3-pi (>=1.21.0,<1.22.0)", "mypy-boto3-pinpoint (>=1.21.0,<1.22.0)", "mypy-boto3-pinpoint-email (>=1.21.0,<1.22.0)", "mypy-boto3-pinpoint-sms-voice (>=1.21.0,<1.22.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.21.0,<1.22.0)", "mypy-boto3-polly (>=1.21.0,<1.22.0)", "mypy-boto3-pricing (>=1.21.0,<1.22.0)", "mypy-boto3-proton (>=1.21.0,<1.22.0)", "mypy-boto3-qldb (>=1.21.0,<1.22.0)", "mypy-boto3-qldb-session (>=1.21.0,<1.22.0)", "mypy-boto3-quicksight (>=1.21.0,<1.22.0)", "mypy-boto3-ram (>=1.21.0,<1.22.0)", "mypy-boto3-rbin (>=1.21.0,<1.22.0)", "mypy-boto3-rds (>=1.21.0,<1.22.0)", "mypy-boto3-rds-data (>=1.21.0,<1.22.0)", "mypy-boto3-redshift (>=1.21.0,<1.22.0)", "mypy-boto3-redshift-data (>=1.21.0,<1.22.0)", "mypy-boto3-rekognition (>=1.21.0,<1.22.0)", "mypy-boto3-resiliencehub (>=1.21.0,<1.22.0)", "mypy-boto3-resource-groups (>=1.21.0,<1.22.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.21.0,<1.22.0)", "mypy-boto3-robomaker (>=1.21.0,<1.22.0)", "mypy-boto3-route53 (>=1.21.0,<1.22.0)", "mypy-boto3-route53-recovery-cluster (>=1.21.0,<1.22.0)", "mypy-boto3-route53-recovery-control-config (>=1.21.0,<1.22.0)", "mypy-boto3-route53-recovery-readiness (>=1.21.0,<1.22.0)", "mypy-boto3-route53domains (>=1.21.0,<1.22.0)", "mypy-boto3-route53resolver (>=1.21.0,<1.22.0)", "mypy-boto3-rum (>=1.21.0,<1.22.0)", "mypy-boto3-s3 (>=1.21.0,<1.22.0)", "mypy-boto3-s3control (>=1.21.0,<1.22.0)", "mypy-boto3-s3outposts (>=1.21.0,<1.22.0)", "mypy-boto3-sagemaker (>=1.21.0,<1.22.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.21.0,<1.22.0)", "mypy-boto3-sagemaker-edge (>=1.21.0,<1.22.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.21.0,<1.22.0)", "mypy-boto3-sagemaker-runtime (>=1.21.0,<1.22.0)", "mypy-boto3-savingsplans (>=1.21.0,<1.22.0)", "mypy-boto3-schemas (>=1.21.0,<1.22.0)", "mypy-boto3-sdb (>=1.21.0,<1.22.0)", "mypy-boto3-secretsmanager (>=1.21.0,<1.22.0)", "mypy-boto3-securityhub (>=1.21.0,<1.22.0)", "mypy-boto3-serverlessrepo (>=1.21.0,<1.22.0)", "mypy-boto3-service-quotas (>=1.21.0,<1.22.0)", "mypy-boto3-servicecatalog (>=1.21.0,<1.22.0)", "mypy-boto3-servicecatalog-appregistry (>=1.21.0,<1.22.0)", "mypy-boto3-servicediscovery (>=1.21.0,<1.22.0)", "mypy-boto3-ses (>=1.21.0,<1.22.0)", "mypy-boto3-sesv2 (>=1.21.0,<1.22.0)", "mypy-boto3-shield (>=1.21.0,<1.22.0)", "mypy-boto3-signer (>=1.21.0,<1.22.0)", "mypy-boto3-sms (>=1.21.0,<1.22.0)", "mypy-boto3-sms-voice (>=1.21.0,<1.22.0)", "mypy-boto3-snow-device-management (>=1.21.0,<1.22.0)", "mypy-boto3-snowball (>=1.21.0,<1.22.0)", "mypy-boto3-sns (>=1.21.0,<1.22.0)", "mypy-boto3-sqs (>=1.21.0,<1.22.0)", "mypy-boto3-ssm (>=1.21.0,<1.22.0)", "mypy-boto3-ssm-contacts (>=1.21.0,<1.22.0)", "mypy-boto3-ssm-incidents (>=1.21.0,<1.22.0)", "mypy-boto3-sso (>=1.21.0,<1.22.0)", "mypy-boto3-sso-admin (>=1.21.0,<1.22.0)", "mypy-boto3-sso-oidc (>=1.21.0,<1.22.0)", "mypy-boto3-stepfunctions (>=1.21.0,<1.22.0)", "mypy-boto3-storagegateway (>=1.21.0,<1.22.0)", "mypy-boto3-sts (>=1.21.0,<1.22.0)", "mypy-boto3-support (>=1.21.0,<1.22.0)", "mypy-boto3-swf (>=1.21.0,<1.22.0)", "mypy-boto3-synthetics (>=1.21.0,<1.22.0)", "mypy-boto3-textract (>=1.21.0,<1.22.0)", "mypy-boto3-timestream-query (>=1.21.0,<1.22.0)", "mypy-boto3-timestream-write (>=1.21.0,<1.22.0)", "mypy-boto3-transcribe (>=1.21.0,<1.22.0)", "mypy-boto3-transfer (>=1.21.0,<1.22.0)", "mypy-boto3-translate (>=1.21.0,<1.22.0)", "mypy-boto3-voice-id (>=1.21.0,<1.22.0)", "mypy-boto3-waf (>=1.21.0,<1.22.0)", "mypy-boto3-waf-regional (>=1.21.0,<1.22.0)", "mypy-boto3-wafv2 (>=1.21.0,<1.22.0)", "mypy-boto3-wellarchitected (>=1.21.0,<1.22.0)", "mypy-boto3-wisdom (>=1.21.0,<1.22.0)", "mypy-boto3-workdocs (>=1.21.0,<1.22.0)", "mypy-boto3-worklink (>=1.21.0,<1.22.0)", "mypy-boto3-workmail (>=1.21.0,<1.22.0)", "mypy-boto3-workmailmessageflow (>=1.21.0,<1.22.0)", "mypy-boto3-workspaces (>=1.21.0,<1.22.0)", "mypy-boto3-workspaces-web (>=1.21.0,<1.22.0)", "mypy-boto3-xray (>=1.21.0,<1.22.0)"] +amp = ["mypy-boto3-amp (>=1.21.0,<1.22.0)"] +amplify = ["mypy-boto3-amplify (>=1.21.0,<1.22.0)"] +amplifybackend = ["mypy-boto3-amplifybackend (>=1.21.0,<1.22.0)"] +amplifyuibuilder = ["mypy-boto3-amplifyuibuilder (>=1.21.0,<1.22.0)"] +apigateway = ["mypy-boto3-apigateway (>=1.21.0,<1.22.0)"] +apigatewaymanagementapi = ["mypy-boto3-apigatewaymanagementapi (>=1.21.0,<1.22.0)"] +apigatewayv2 = ["mypy-boto3-apigatewayv2 (>=1.21.0,<1.22.0)"] +appconfig = ["mypy-boto3-appconfig (>=1.21.0,<1.22.0)"] +appconfigdata = ["mypy-boto3-appconfigdata (>=1.21.0,<1.22.0)"] +appflow = ["mypy-boto3-appflow (>=1.21.0,<1.22.0)"] +appintegrations = ["mypy-boto3-appintegrations (>=1.21.0,<1.22.0)"] +application-autoscaling = ["mypy-boto3-application-autoscaling (>=1.21.0,<1.22.0)"] +application-insights = ["mypy-boto3-application-insights (>=1.21.0,<1.22.0)"] +applicationcostprofiler = ["mypy-boto3-applicationcostprofiler (>=1.21.0,<1.22.0)"] +appmesh = ["mypy-boto3-appmesh (>=1.21.0,<1.22.0)"] +apprunner = ["mypy-boto3-apprunner (>=1.21.0,<1.22.0)"] +appstream = ["mypy-boto3-appstream (>=1.21.0,<1.22.0)"] +appsync = ["mypy-boto3-appsync (>=1.21.0,<1.22.0)"] +athena = ["mypy-boto3-athena (>=1.21.0,<1.22.0)"] +auditmanager = ["mypy-boto3-auditmanager (>=1.21.0,<1.22.0)"] +autoscaling = ["mypy-boto3-autoscaling (>=1.21.0,<1.22.0)"] +autoscaling-plans = ["mypy-boto3-autoscaling-plans (>=1.21.0,<1.22.0)"] +backup = ["mypy-boto3-backup (>=1.21.0,<1.22.0)"] +backup-gateway = ["mypy-boto3-backup-gateway (>=1.21.0,<1.22.0)"] +batch = ["mypy-boto3-batch (>=1.21.0,<1.22.0)"] +billingconductor = ["mypy-boto3-billingconductor (>=1.21.0,<1.22.0)"] +braket = ["mypy-boto3-braket (>=1.21.0,<1.22.0)"] +budgets = ["mypy-boto3-budgets (>=1.21.0,<1.22.0)"] +ce = ["mypy-boto3-ce (>=1.21.0,<1.22.0)"] +chime = ["mypy-boto3-chime (>=1.21.0,<1.22.0)"] +chime-sdk-identity = ["mypy-boto3-chime-sdk-identity (>=1.21.0,<1.22.0)"] +chime-sdk-meetings = ["mypy-boto3-chime-sdk-meetings (>=1.21.0,<1.22.0)"] +chime-sdk-messaging = ["mypy-boto3-chime-sdk-messaging (>=1.21.0,<1.22.0)"] +cloud9 = ["mypy-boto3-cloud9 (>=1.21.0,<1.22.0)"] +cloudcontrol = ["mypy-boto3-cloudcontrol (>=1.21.0,<1.22.0)"] +clouddirectory = ["mypy-boto3-clouddirectory (>=1.21.0,<1.22.0)"] +cloudformation = ["mypy-boto3-cloudformation (>=1.21.0,<1.22.0)"] +cloudfront = ["mypy-boto3-cloudfront (>=1.21.0,<1.22.0)"] +cloudhsm = ["mypy-boto3-cloudhsm (>=1.21.0,<1.22.0)"] +cloudhsmv2 = ["mypy-boto3-cloudhsmv2 (>=1.21.0,<1.22.0)"] +cloudsearch = ["mypy-boto3-cloudsearch (>=1.21.0,<1.22.0)"] +cloudsearchdomain = ["mypy-boto3-cloudsearchdomain (>=1.21.0,<1.22.0)"] +cloudtrail = ["mypy-boto3-cloudtrail (>=1.21.0,<1.22.0)"] +cloudwatch = ["mypy-boto3-cloudwatch (>=1.21.0,<1.22.0)"] +codeartifact = ["mypy-boto3-codeartifact (>=1.21.0,<1.22.0)"] +codebuild = ["mypy-boto3-codebuild (>=1.21.0,<1.22.0)"] +codecommit = ["mypy-boto3-codecommit (>=1.21.0,<1.22.0)"] +codedeploy = ["mypy-boto3-codedeploy (>=1.21.0,<1.22.0)"] +codeguru-reviewer = ["mypy-boto3-codeguru-reviewer (>=1.21.0,<1.22.0)"] +codeguruprofiler = ["mypy-boto3-codeguruprofiler (>=1.21.0,<1.22.0)"] +codepipeline = ["mypy-boto3-codepipeline (>=1.21.0,<1.22.0)"] +codestar = ["mypy-boto3-codestar (>=1.21.0,<1.22.0)"] +codestar-connections = ["mypy-boto3-codestar-connections (>=1.21.0,<1.22.0)"] +codestar-notifications = ["mypy-boto3-codestar-notifications (>=1.21.0,<1.22.0)"] +cognito-identity = ["mypy-boto3-cognito-identity (>=1.21.0,<1.22.0)"] +cognito-idp = ["mypy-boto3-cognito-idp (>=1.21.0,<1.22.0)"] +cognito-sync = ["mypy-boto3-cognito-sync (>=1.21.0,<1.22.0)"] +comprehend = ["mypy-boto3-comprehend (>=1.21.0,<1.22.0)"] +comprehendmedical = ["mypy-boto3-comprehendmedical (>=1.21.0,<1.22.0)"] +compute-optimizer = ["mypy-boto3-compute-optimizer (>=1.21.0,<1.22.0)"] +config = ["mypy-boto3-config (>=1.21.0,<1.22.0)"] +connect = ["mypy-boto3-connect (>=1.21.0,<1.22.0)"] +connect-contact-lens = ["mypy-boto3-connect-contact-lens (>=1.21.0,<1.22.0)"] +connectparticipant = ["mypy-boto3-connectparticipant (>=1.21.0,<1.22.0)"] +cur = ["mypy-boto3-cur (>=1.21.0,<1.22.0)"] +customer-profiles = ["mypy-boto3-customer-profiles (>=1.21.0,<1.22.0)"] +databrew = ["mypy-boto3-databrew (>=1.21.0,<1.22.0)"] +dataexchange = ["mypy-boto3-dataexchange (>=1.21.0,<1.22.0)"] +datapipeline = ["mypy-boto3-datapipeline (>=1.21.0,<1.22.0)"] +datasync = ["mypy-boto3-datasync (>=1.21.0,<1.22.0)"] +dax = ["mypy-boto3-dax (>=1.21.0,<1.22.0)"] +detective = ["mypy-boto3-detective (>=1.21.0,<1.22.0)"] +devicefarm = ["mypy-boto3-devicefarm (>=1.21.0,<1.22.0)"] +devops-guru = ["mypy-boto3-devops-guru (>=1.21.0,<1.22.0)"] +directconnect = ["mypy-boto3-directconnect (>=1.21.0,<1.22.0)"] +discovery = ["mypy-boto3-discovery (>=1.21.0,<1.22.0)"] +dlm = ["mypy-boto3-dlm (>=1.21.0,<1.22.0)"] +dms = ["mypy-boto3-dms (>=1.21.0,<1.22.0)"] +docdb = ["mypy-boto3-docdb (>=1.21.0,<1.22.0)"] +drs = ["mypy-boto3-drs (>=1.21.0,<1.22.0)"] +ds = ["mypy-boto3-ds (>=1.21.0,<1.22.0)"] +dynamodb = ["mypy-boto3-dynamodb (>=1.21.0,<1.22.0)"] +dynamodbstreams = ["mypy-boto3-dynamodbstreams (>=1.21.0,<1.22.0)"] +ebs = ["mypy-boto3-ebs (>=1.21.0,<1.22.0)"] +ec2 = ["mypy-boto3-ec2 (>=1.21.0,<1.22.0)"] +ec2-instance-connect = ["mypy-boto3-ec2-instance-connect (>=1.21.0,<1.22.0)"] +ecr = ["mypy-boto3-ecr (>=1.21.0,<1.22.0)"] +ecr-public = ["mypy-boto3-ecr-public (>=1.21.0,<1.22.0)"] +ecs = ["mypy-boto3-ecs (>=1.21.0,<1.22.0)"] +efs = ["mypy-boto3-efs (>=1.21.0,<1.22.0)"] +eks = ["mypy-boto3-eks (>=1.21.0,<1.22.0)"] +elastic-inference = ["mypy-boto3-elastic-inference (>=1.21.0,<1.22.0)"] +elasticache = ["mypy-boto3-elasticache (>=1.21.0,<1.22.0)"] +elasticbeanstalk = ["mypy-boto3-elasticbeanstalk (>=1.21.0,<1.22.0)"] +elastictranscoder = ["mypy-boto3-elastictranscoder (>=1.21.0,<1.22.0)"] +elb = ["mypy-boto3-elb (>=1.21.0,<1.22.0)"] +elbv2 = ["mypy-boto3-elbv2 (>=1.21.0,<1.22.0)"] +emr = ["mypy-boto3-emr (>=1.21.0,<1.22.0)"] +emr-containers = ["mypy-boto3-emr-containers (>=1.21.0,<1.22.0)"] +es = ["mypy-boto3-es (>=1.21.0,<1.22.0)"] +essential = ["mypy-boto3-cloudformation (>=1.21.0,<1.22.0)", "mypy-boto3-dynamodb (>=1.21.0,<1.22.0)", "mypy-boto3-ec2 (>=1.21.0,<1.22.0)", "mypy-boto3-lambda (>=1.21.0,<1.22.0)", "mypy-boto3-rds (>=1.21.0,<1.22.0)", "mypy-boto3-s3 (>=1.21.0,<1.22.0)", "mypy-boto3-sqs (>=1.21.0,<1.22.0)"] +events = ["mypy-boto3-events (>=1.21.0,<1.22.0)"] +evidently = ["mypy-boto3-evidently (>=1.21.0,<1.22.0)"] +finspace = ["mypy-boto3-finspace (>=1.21.0,<1.22.0)"] +finspace-data = ["mypy-boto3-finspace-data (>=1.21.0,<1.22.0)"] +firehose = ["mypy-boto3-firehose (>=1.21.0,<1.22.0)"] +fis = ["mypy-boto3-fis (>=1.21.0,<1.22.0)"] +fms = ["mypy-boto3-fms (>=1.21.0,<1.22.0)"] +forecast = ["mypy-boto3-forecast (>=1.21.0,<1.22.0)"] +forecastquery = ["mypy-boto3-forecastquery (>=1.21.0,<1.22.0)"] +frauddetector = ["mypy-boto3-frauddetector (>=1.21.0,<1.22.0)"] +fsx = ["mypy-boto3-fsx (>=1.21.0,<1.22.0)"] +gamelift = ["mypy-boto3-gamelift (>=1.21.0,<1.22.0)"] +gamesparks = ["mypy-boto3-gamesparks (>=1.21.0,<1.22.0)"] +glacier = ["mypy-boto3-glacier (>=1.21.0,<1.22.0)"] +globalaccelerator = ["mypy-boto3-globalaccelerator (>=1.21.0,<1.22.0)"] +glue = ["mypy-boto3-glue (>=1.21.0,<1.22.0)"] +grafana = ["mypy-boto3-grafana (>=1.21.0,<1.22.0)"] +greengrass = ["mypy-boto3-greengrass (>=1.21.0,<1.22.0)"] +greengrassv2 = ["mypy-boto3-greengrassv2 (>=1.21.0,<1.22.0)"] +groundstation = ["mypy-boto3-groundstation (>=1.21.0,<1.22.0)"] +guardduty = ["mypy-boto3-guardduty (>=1.21.0,<1.22.0)"] +health = ["mypy-boto3-health (>=1.21.0,<1.22.0)"] +healthlake = ["mypy-boto3-healthlake (>=1.21.0,<1.22.0)"] +honeycode = ["mypy-boto3-honeycode (>=1.21.0,<1.22.0)"] +iam = ["mypy-boto3-iam (>=1.21.0,<1.22.0)"] +identitystore = ["mypy-boto3-identitystore (>=1.21.0,<1.22.0)"] +imagebuilder = ["mypy-boto3-imagebuilder (>=1.21.0,<1.22.0)"] +importexport = ["mypy-boto3-importexport (>=1.21.0,<1.22.0)"] +inspector = ["mypy-boto3-inspector (>=1.21.0,<1.22.0)"] +inspector2 = ["mypy-boto3-inspector2 (>=1.21.0,<1.22.0)"] +iot = ["mypy-boto3-iot (>=1.21.0,<1.22.0)"] +iot-data = ["mypy-boto3-iot-data (>=1.21.0,<1.22.0)"] +iot-jobs-data = ["mypy-boto3-iot-jobs-data (>=1.21.0,<1.22.0)"] +iot1click-devices = ["mypy-boto3-iot1click-devices (>=1.21.0,<1.22.0)"] +iot1click-projects = ["mypy-boto3-iot1click-projects (>=1.21.0,<1.22.0)"] +iotanalytics = ["mypy-boto3-iotanalytics (>=1.21.0,<1.22.0)"] +iotdeviceadvisor = ["mypy-boto3-iotdeviceadvisor (>=1.21.0,<1.22.0)"] +iotevents = ["mypy-boto3-iotevents (>=1.21.0,<1.22.0)"] +iotevents-data = ["mypy-boto3-iotevents-data (>=1.21.0,<1.22.0)"] +iotfleethub = ["mypy-boto3-iotfleethub (>=1.21.0,<1.22.0)"] +iotsecuretunneling = ["mypy-boto3-iotsecuretunneling (>=1.21.0,<1.22.0)"] +iotsitewise = ["mypy-boto3-iotsitewise (>=1.21.0,<1.22.0)"] +iotthingsgraph = ["mypy-boto3-iotthingsgraph (>=1.21.0,<1.22.0)"] +iottwinmaker = ["mypy-boto3-iottwinmaker (>=1.21.0,<1.22.0)"] +iotwireless = ["mypy-boto3-iotwireless (>=1.21.0,<1.22.0)"] +ivs = ["mypy-boto3-ivs (>=1.21.0,<1.22.0)"] +kafka = ["mypy-boto3-kafka (>=1.21.0,<1.22.0)"] +kafkaconnect = ["mypy-boto3-kafkaconnect (>=1.21.0,<1.22.0)"] +kendra = ["mypy-boto3-kendra (>=1.21.0,<1.22.0)"] +keyspaces = ["mypy-boto3-keyspaces (>=1.21.0,<1.22.0)"] +kinesis = ["mypy-boto3-kinesis (>=1.21.0,<1.22.0)"] +kinesis-video-archived-media = ["mypy-boto3-kinesis-video-archived-media (>=1.21.0,<1.22.0)"] +kinesis-video-media = ["mypy-boto3-kinesis-video-media (>=1.21.0,<1.22.0)"] +kinesis-video-signaling = ["mypy-boto3-kinesis-video-signaling (>=1.21.0,<1.22.0)"] +kinesisanalytics = ["mypy-boto3-kinesisanalytics (>=1.21.0,<1.22.0)"] +kinesisanalyticsv2 = ["mypy-boto3-kinesisanalyticsv2 (>=1.21.0,<1.22.0)"] +kinesisvideo = ["mypy-boto3-kinesisvideo (>=1.21.0,<1.22.0)"] +kms = ["mypy-boto3-kms (>=1.21.0,<1.22.0)"] +lakeformation = ["mypy-boto3-lakeformation (>=1.21.0,<1.22.0)"] +lambda = ["mypy-boto3-lambda (>=1.21.0,<1.22.0)"] +lex-models = ["mypy-boto3-lex-models (>=1.21.0,<1.22.0)"] +lex-runtime = ["mypy-boto3-lex-runtime (>=1.21.0,<1.22.0)"] +lexv2-models = ["mypy-boto3-lexv2-models (>=1.21.0,<1.22.0)"] +lexv2-runtime = ["mypy-boto3-lexv2-runtime (>=1.21.0,<1.22.0)"] +license-manager = ["mypy-boto3-license-manager (>=1.21.0,<1.22.0)"] +lightsail = ["mypy-boto3-lightsail (>=1.21.0,<1.22.0)"] +location = ["mypy-boto3-location (>=1.21.0,<1.22.0)"] +logs = ["mypy-boto3-logs (>=1.21.0,<1.22.0)"] +lookoutequipment = ["mypy-boto3-lookoutequipment (>=1.21.0,<1.22.0)"] +lookoutmetrics = ["mypy-boto3-lookoutmetrics (>=1.21.0,<1.22.0)"] +lookoutvision = ["mypy-boto3-lookoutvision (>=1.21.0,<1.22.0)"] +machinelearning = ["mypy-boto3-machinelearning (>=1.21.0,<1.22.0)"] +macie = ["mypy-boto3-macie (>=1.21.0,<1.22.0)"] +macie2 = ["mypy-boto3-macie2 (>=1.21.0,<1.22.0)"] +managedblockchain = ["mypy-boto3-managedblockchain (>=1.21.0,<1.22.0)"] +marketplace-catalog = ["mypy-boto3-marketplace-catalog (>=1.21.0,<1.22.0)"] +marketplace-entitlement = ["mypy-boto3-marketplace-entitlement (>=1.21.0,<1.22.0)"] +marketplacecommerceanalytics = ["mypy-boto3-marketplacecommerceanalytics (>=1.21.0,<1.22.0)"] +mediaconnect = ["mypy-boto3-mediaconnect (>=1.21.0,<1.22.0)"] +mediaconvert = ["mypy-boto3-mediaconvert (>=1.21.0,<1.22.0)"] +medialive = ["mypy-boto3-medialive (>=1.21.0,<1.22.0)"] +mediapackage = ["mypy-boto3-mediapackage (>=1.21.0,<1.22.0)"] +mediapackage-vod = ["mypy-boto3-mediapackage-vod (>=1.21.0,<1.22.0)"] +mediastore = ["mypy-boto3-mediastore (>=1.21.0,<1.22.0)"] +mediastore-data = ["mypy-boto3-mediastore-data (>=1.21.0,<1.22.0)"] +mediatailor = ["mypy-boto3-mediatailor (>=1.21.0,<1.22.0)"] +memorydb = ["mypy-boto3-memorydb (>=1.21.0,<1.22.0)"] +meteringmarketplace = ["mypy-boto3-meteringmarketplace (>=1.21.0,<1.22.0)"] +mgh = ["mypy-boto3-mgh (>=1.21.0,<1.22.0)"] +mgn = ["mypy-boto3-mgn (>=1.21.0,<1.22.0)"] +migration-hub-refactor-spaces = ["mypy-boto3-migration-hub-refactor-spaces (>=1.21.0,<1.22.0)"] +migrationhub-config = ["mypy-boto3-migrationhub-config (>=1.21.0,<1.22.0)"] +migrationhubstrategy = ["mypy-boto3-migrationhubstrategy (>=1.21.0,<1.22.0)"] +mobile = ["mypy-boto3-mobile (>=1.21.0,<1.22.0)"] +mq = ["mypy-boto3-mq (>=1.21.0,<1.22.0)"] +mturk = ["mypy-boto3-mturk (>=1.21.0,<1.22.0)"] +mwaa = ["mypy-boto3-mwaa (>=1.21.0,<1.22.0)"] +neptune = ["mypy-boto3-neptune (>=1.21.0,<1.22.0)"] +network-firewall = ["mypy-boto3-network-firewall (>=1.21.0,<1.22.0)"] +networkmanager = ["mypy-boto3-networkmanager (>=1.21.0,<1.22.0)"] +nimble = ["mypy-boto3-nimble (>=1.21.0,<1.22.0)"] +opensearch = ["mypy-boto3-opensearch (>=1.21.0,<1.22.0)"] +opsworks = ["mypy-boto3-opsworks (>=1.21.0,<1.22.0)"] +opsworkscm = ["mypy-boto3-opsworkscm (>=1.21.0,<1.22.0)"] +organizations = ["mypy-boto3-organizations (>=1.21.0,<1.22.0)"] +outposts = ["mypy-boto3-outposts (>=1.21.0,<1.22.0)"] +panorama = ["mypy-boto3-panorama (>=1.21.0,<1.22.0)"] +personalize = ["mypy-boto3-personalize (>=1.21.0,<1.22.0)"] +personalize-events = ["mypy-boto3-personalize-events (>=1.21.0,<1.22.0)"] +personalize-runtime = ["mypy-boto3-personalize-runtime (>=1.21.0,<1.22.0)"] +pi = ["mypy-boto3-pi (>=1.21.0,<1.22.0)"] +pinpoint = ["mypy-boto3-pinpoint (>=1.21.0,<1.22.0)"] +pinpoint-email = ["mypy-boto3-pinpoint-email (>=1.21.0,<1.22.0)"] +pinpoint-sms-voice = ["mypy-boto3-pinpoint-sms-voice (>=1.21.0,<1.22.0)"] +pinpoint-sms-voice-v2 = ["mypy-boto3-pinpoint-sms-voice-v2 (>=1.21.0,<1.22.0)"] +polly = ["mypy-boto3-polly (>=1.21.0,<1.22.0)"] +pricing = ["mypy-boto3-pricing (>=1.21.0,<1.22.0)"] +proton = ["mypy-boto3-proton (>=1.21.0,<1.22.0)"] +qldb = ["mypy-boto3-qldb (>=1.21.0,<1.22.0)"] +qldb-session = ["mypy-boto3-qldb-session (>=1.21.0,<1.22.0)"] +quicksight = ["mypy-boto3-quicksight (>=1.21.0,<1.22.0)"] +ram = ["mypy-boto3-ram (>=1.21.0,<1.22.0)"] +rbin = ["mypy-boto3-rbin (>=1.21.0,<1.22.0)"] +rds = ["mypy-boto3-rds (>=1.21.0,<1.22.0)"] +rds-data = ["mypy-boto3-rds-data (>=1.21.0,<1.22.0)"] +redshift = ["mypy-boto3-redshift (>=1.21.0,<1.22.0)"] +redshift-data = ["mypy-boto3-redshift-data (>=1.21.0,<1.22.0)"] +rekognition = ["mypy-boto3-rekognition (>=1.21.0,<1.22.0)"] +resiliencehub = ["mypy-boto3-resiliencehub (>=1.21.0,<1.22.0)"] +resource-groups = ["mypy-boto3-resource-groups (>=1.21.0,<1.22.0)"] +resourcegroupstaggingapi = ["mypy-boto3-resourcegroupstaggingapi (>=1.21.0,<1.22.0)"] +robomaker = ["mypy-boto3-robomaker (>=1.21.0,<1.22.0)"] +route53 = ["mypy-boto3-route53 (>=1.21.0,<1.22.0)"] +route53-recovery-cluster = ["mypy-boto3-route53-recovery-cluster (>=1.21.0,<1.22.0)"] +route53-recovery-control-config = ["mypy-boto3-route53-recovery-control-config (>=1.21.0,<1.22.0)"] +route53-recovery-readiness = ["mypy-boto3-route53-recovery-readiness (>=1.21.0,<1.22.0)"] +route53domains = ["mypy-boto3-route53domains (>=1.21.0,<1.22.0)"] +route53resolver = ["mypy-boto3-route53resolver (>=1.21.0,<1.22.0)"] +rum = ["mypy-boto3-rum (>=1.21.0,<1.22.0)"] +s3 = ["mypy-boto3-s3 (>=1.21.0,<1.22.0)"] +s3control = ["mypy-boto3-s3control (>=1.21.0,<1.22.0)"] +s3outposts = ["mypy-boto3-s3outposts (>=1.21.0,<1.22.0)"] +sagemaker = ["mypy-boto3-sagemaker (>=1.21.0,<1.22.0)"] +sagemaker-a2i-runtime = ["mypy-boto3-sagemaker-a2i-runtime (>=1.21.0,<1.22.0)"] +sagemaker-edge = ["mypy-boto3-sagemaker-edge (>=1.21.0,<1.22.0)"] +sagemaker-featurestore-runtime = ["mypy-boto3-sagemaker-featurestore-runtime (>=1.21.0,<1.22.0)"] +sagemaker-runtime = ["mypy-boto3-sagemaker-runtime (>=1.21.0,<1.22.0)"] +savingsplans = ["mypy-boto3-savingsplans (>=1.21.0,<1.22.0)"] +schemas = ["mypy-boto3-schemas (>=1.21.0,<1.22.0)"] +sdb = ["mypy-boto3-sdb (>=1.21.0,<1.22.0)"] +secretsmanager = ["mypy-boto3-secretsmanager (>=1.21.0,<1.22.0)"] +securityhub = ["mypy-boto3-securityhub (>=1.21.0,<1.22.0)"] +serverlessrepo = ["mypy-boto3-serverlessrepo (>=1.21.0,<1.22.0)"] +service-quotas = ["mypy-boto3-service-quotas (>=1.21.0,<1.22.0)"] +servicecatalog = ["mypy-boto3-servicecatalog (>=1.21.0,<1.22.0)"] +servicecatalog-appregistry = ["mypy-boto3-servicecatalog-appregistry (>=1.21.0,<1.22.0)"] +servicediscovery = ["mypy-boto3-servicediscovery (>=1.21.0,<1.22.0)"] +ses = ["mypy-boto3-ses (>=1.21.0,<1.22.0)"] +sesv2 = ["mypy-boto3-sesv2 (>=1.21.0,<1.22.0)"] +shield = ["mypy-boto3-shield (>=1.21.0,<1.22.0)"] +signer = ["mypy-boto3-signer (>=1.21.0,<1.22.0)"] +sms = ["mypy-boto3-sms (>=1.21.0,<1.22.0)"] +sms-voice = ["mypy-boto3-sms-voice (>=1.21.0,<1.22.0)"] +snow-device-management = ["mypy-boto3-snow-device-management (>=1.21.0,<1.22.0)"] +snowball = ["mypy-boto3-snowball (>=1.21.0,<1.22.0)"] +sns = ["mypy-boto3-sns (>=1.21.0,<1.22.0)"] +sqs = ["mypy-boto3-sqs (>=1.21.0,<1.22.0)"] +ssm = ["mypy-boto3-ssm (>=1.21.0,<1.22.0)"] +ssm-contacts = ["mypy-boto3-ssm-contacts (>=1.21.0,<1.22.0)"] +ssm-incidents = ["mypy-boto3-ssm-incidents (>=1.21.0,<1.22.0)"] +sso = ["mypy-boto3-sso (>=1.21.0,<1.22.0)"] +sso-admin = ["mypy-boto3-sso-admin (>=1.21.0,<1.22.0)"] +sso-oidc = ["mypy-boto3-sso-oidc (>=1.21.0,<1.22.0)"] +stepfunctions = ["mypy-boto3-stepfunctions (>=1.21.0,<1.22.0)"] +storagegateway = ["mypy-boto3-storagegateway (>=1.21.0,<1.22.0)"] +sts = ["mypy-boto3-sts (>=1.21.0,<1.22.0)"] +support = ["mypy-boto3-support (>=1.21.0,<1.22.0)"] +swf = ["mypy-boto3-swf (>=1.21.0,<1.22.0)"] +synthetics = ["mypy-boto3-synthetics (>=1.21.0,<1.22.0)"] +textract = ["mypy-boto3-textract (>=1.21.0,<1.22.0)"] +timestream-query = ["mypy-boto3-timestream-query (>=1.21.0,<1.22.0)"] +timestream-write = ["mypy-boto3-timestream-write (>=1.21.0,<1.22.0)"] +transcribe = ["mypy-boto3-transcribe (>=1.21.0,<1.22.0)"] +transfer = ["mypy-boto3-transfer (>=1.21.0,<1.22.0)"] +translate = ["mypy-boto3-translate (>=1.21.0,<1.22.0)"] +voice-id = ["mypy-boto3-voice-id (>=1.21.0,<1.22.0)"] +waf = ["mypy-boto3-waf (>=1.21.0,<1.22.0)"] +waf-regional = ["mypy-boto3-waf-regional (>=1.21.0,<1.22.0)"] +wafv2 = ["mypy-boto3-wafv2 (>=1.21.0,<1.22.0)"] +wellarchitected = ["mypy-boto3-wellarchitected (>=1.21.0,<1.22.0)"] +wisdom = ["mypy-boto3-wisdom (>=1.21.0,<1.22.0)"] +workdocs = ["mypy-boto3-workdocs (>=1.21.0,<1.22.0)"] +worklink = ["mypy-boto3-worklink (>=1.21.0,<1.22.0)"] +workmail = ["mypy-boto3-workmail (>=1.21.0,<1.22.0)"] +workmailmessageflow = ["mypy-boto3-workmailmessageflow (>=1.21.0,<1.22.0)"] +workspaces = ["mypy-boto3-workspaces (>=1.21.0,<1.22.0)"] +workspaces-web = ["mypy-boto3-workspaces-web (>=1.21.0,<1.22.0)"] +xray = ["mypy-boto3-xray (>=1.21.0,<1.22.0)"] + [[package]] name = "botocore" version = "1.23.17" @@ -107,6 +429,17 @@ urllib3 = ">=1.25.4,<1.27" [package.extras] crt = ["awscrt (==0.12.5)"] +[[package]] +name = "botocore-stubs" +version = "1.24.37" +description = "Type annotations for botocore 1.24.37 generated with mypy-boto3-builder 7.5.8" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = ">=4.1.0" + [[package]] name = "certifi" version = "2021.10.8" @@ -613,6 +946,39 @@ typing-extensions = ">=3.10" dmypy = ["psutil (>=4.0)"] python2 = ["typed-ast (>=1.4.0,<2)"] +[[package]] +name = "mypy-boto3-appconfig" +version = "1.21.34" +description = "Type annotations for boto3.AppConfig 1.21.34 service generated with mypy-boto3-builder 7.5.8" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = ">=4.1.0" + +[[package]] +name = "mypy-boto3-secretsmanager" +version = "1.21.34" +description = "Type annotations for boto3.SecretsManager 1.21.34 service generated with mypy-boto3-builder 7.5.8" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = ">=4.1.0" + +[[package]] +name = "mypy-boto3-ssm" +version = "1.21.34" +description = "Type annotations for boto3.SSM 1.21.34 service generated with mypy-boto3-builder 7.5.8" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = ">=4.1.0" + [[package]] name = "mypy-extensions" version = "0.4.3" @@ -987,7 +1353,7 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "4.0.1" +version = "4.1.1" description = "Backported and Experimental Type Hints for Python 3.6+" category = "main" optional = false @@ -1056,7 +1422,7 @@ pydantic = ["pydantic", "email-validator"] [metadata] lock-version = "1.1" python-versions = "^3.6.2" -content-hash = "57759324149561b2a8c1f2564a3826bb0ee744a9f8a2a8d099dcbd50cf452ef8" +content-hash = "acfdb55ba6d2e7611a405cdb9ebabe4223a39b87b6c447a43c9c6254370094b4" [metadata.files] atomicwrites = [ @@ -1083,10 +1449,18 @@ boto3 = [ {file = "boto3-1.20.17-py3-none-any.whl", hash = "sha256:b832c75386a4c5b7194acea1ae82dc309fddd69e660731350235d19cf70d8014"}, {file = "boto3-1.20.17.tar.gz", hash = "sha256:41ea196ff71ee0255ad164790319ec158fd5048de915173e8b21226650a0512f"}, ] +boto3-stubs = [ + {file = "boto3-stubs-1.21.37.tar.gz", hash = "sha256:1fa208a4a2d91550b031e82037397eb9a5fcfd0295f4795bc412219e86be1e8b"}, + {file = "boto3_stubs-1.21.37-py3-none-any.whl", hash = "sha256:df790210ef992dfcee47d4d83a848f017380b58266cba6a50cfd8fbe6e6839ef"}, +] botocore = [ {file = "botocore-1.23.17-py3-none-any.whl", hash = "sha256:54240370476d8e67a97664d2c47df451f0e1d30e9d50ea0a88da4c2c27981159"}, {file = "botocore-1.23.17.tar.gz", hash = "sha256:a9753b5220b5cc1bb8078086dc8ee10aa7da482b279dd0347965e9145a557003"}, ] +botocore-stubs = [ + {file = "botocore-stubs-1.24.37.tar.gz", hash = "sha256:4221071ea3fea9b5f6840ea848c74fd1ab04d64b9e7acd4371b30468d21fbc9f"}, + {file = "botocore_stubs-1.24.37-py3-none-any.whl", hash = "sha256:beec7e6a6dee17d70519a1cd7446738eeaf78009362b394ff9f075a0e6229fae"}, +] certifi = [ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, @@ -1262,28 +1636,12 @@ markdown = [ {file = "Markdown-3.3.5.tar.gz", hash = "sha256:26e9546bfbcde5fcd072bd8f612c9c1b6e2677cb8aadbdf65206674f46dde069"}, ] markupsafe = [ - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, @@ -1292,27 +1650,14 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, @@ -1322,12 +1667,6 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, @@ -1382,6 +1721,18 @@ mypy = [ {file = "mypy-0.931-py3-none-any.whl", hash = "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d"}, {file = "mypy-0.931.tar.gz", hash = "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce"}, ] +mypy-boto3-appconfig = [ + {file = "mypy-boto3-appconfig-1.21.34.tar.gz", hash = "sha256:620350b576e7a5df62b30829f5b9760e4aa105e1b7b2455cccb2be32e529e950"}, + {file = "mypy_boto3_appconfig-1.21.34-py3-none-any.whl", hash = "sha256:1c9c20e5c9436f65989e35692497a3c1c19d6b28aeb9d6989cee3b0cfb1b3f7d"}, +] +mypy-boto3-secretsmanager = [ + {file = "mypy-boto3-secretsmanager-1.21.34.tar.gz", hash = "sha256:91f517ef0b3e1fb668db873146b8404fd6926c300ba0cef8df847e8929b4b4e5"}, + {file = "mypy_boto3_secretsmanager-1.21.34-py3-none-any.whl", hash = "sha256:534fed05d1a5b5caf7032a88f295d58addf1135905d1559a87109a41e34b3b23"}, +] +mypy-boto3-ssm = [ + {file = "mypy-boto3-ssm-1.21.34.tar.gz", hash = "sha256:cfc59e6c0513a3b72e3036a1785fcb57a1c4563db0e05515b0f082a34e0981c3"}, + {file = "mypy_boto3_ssm-1.21.34-py3-none-any.whl", hash = "sha256:f799b77d611c4d84d2915957423d06de8ee0c270d385374387c7b022f5941b34"}, +] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, @@ -1626,8 +1977,8 @@ typed-ast = [ {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typing-extensions = [ - {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, - {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, + {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, + {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, ] urllib3 = [ {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, diff --git a/pyproject.toml b/pyproject.toml index 78905ec4a6b..800993d8574 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ boto3 = "^1.18" jmespath = "^0.10.0" pydantic = {version = "^1.8.2", optional = true } email-validator = {version = "*", optional = true } +boto3-stubs = {extras = ["appconfig", "ssm", "secretsmanager"], version = "^1.21.37"} [tool.poetry.dev-dependencies] coverage = {extras = ["toml"], version = "^6.2"} From cd426fe96f46f1f8a2838fb5ab1d2c41ccb9c77b Mon Sep 17 00:00:00 2001 From: Ran Isenberg Date: Mon, 18 Apr 2022 16:28:02 +0300 Subject: [PATCH 06/18] cr fixes --- .../utilities/parameters/appconfig.py | 8 +- .../utilities/parameters/secrets.py | 8 +- .../utilities/parameters/ssm.py | 8 +- poetry.lock | 88 +++++++++++-------- pyproject.toml | 4 +- 5 files changed, 67 insertions(+), 49 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index ebba175f601..37f7ef3b3c3 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -4,12 +4,14 @@ import os -from typing import Any, Dict, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, Optional, Union from uuid import uuid4 import boto3 from botocore.config import Config -from mypy_boto3_appconfig import AppConfigClient + +if TYPE_CHECKING: + from mypy_boto3_appconfig import AppConfigClient from ...shared import constants from ...shared.functions import resolve_env_var_choice @@ -71,7 +73,7 @@ def __init__( application: Optional[str] = None, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None, - boto3_client: Optional[AppConfigClient] = None, + boto3_client: Optional["AppConfigClient"] = None, ): """ Initialize the App Config client diff --git a/aws_lambda_powertools/utilities/parameters/secrets.py b/aws_lambda_powertools/utilities/parameters/secrets.py index 5bb6fe34027..a084e53935b 100644 --- a/aws_lambda_powertools/utilities/parameters/secrets.py +++ b/aws_lambda_powertools/utilities/parameters/secrets.py @@ -3,11 +3,13 @@ """ -from typing import Any, Dict, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, Optional, Union import boto3 from botocore.config import Config -from mypy_boto3_secretsmanager import SecretsManagerClient + +if TYPE_CHECKING: + from mypy_boto3_secretsmanager import SecretsManagerClient from .base import DEFAULT_MAX_AGE_SECS, DEFAULT_PROVIDERS, BaseProvider @@ -67,7 +69,7 @@ def __init__( self, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None, - boto3_client: Optional[SecretsManagerClient] = None, + boto3_client: Optional["SecretsManagerClient"] = None, ): """ Initialize the Secrets Manager client diff --git a/aws_lambda_powertools/utilities/parameters/ssm.py b/aws_lambda_powertools/utilities/parameters/ssm.py index 94ac31d5fe8..72a87c37936 100644 --- a/aws_lambda_powertools/utilities/parameters/ssm.py +++ b/aws_lambda_powertools/utilities/parameters/ssm.py @@ -3,14 +3,16 @@ """ -from typing import Any, Dict, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, Optional, Union import boto3 from botocore.config import Config -from mypy_boto3_ssm import SSMClient from .base import DEFAULT_MAX_AGE_SECS, DEFAULT_PROVIDERS, BaseProvider +if TYPE_CHECKING: + from mypy_boto3_ssm import SSMClient + class SSMProvider(BaseProvider): """ @@ -83,7 +85,7 @@ def __init__( self, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None, - boto3_client: Optional[SSMClient] = None, + boto3_client: Optional["SSMClient"] = None, ): """ Initialize the SSM Parameter Store client diff --git a/poetry.lock b/poetry.lock index 9a964e4c45c..3fb7c1ffa64 100644 --- a/poetry.lock +++ b/poetry.lock @@ -613,6 +613,39 @@ typing-extensions = ">=3.10" dmypy = ["psutil (>=4.0)"] python2 = ["typed-ast (>=1.4.0,<2)"] +[[package]] +name = "mypy-boto3-appconfig" +version = "1.21.34" +description = "Type annotations for boto3.AppConfig 1.21.34 service generated with mypy-boto3-builder 7.5.8" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = ">=4.1.0" + +[[package]] +name = "mypy-boto3-secretsmanager" +version = "1.21.34" +description = "Type annotations for boto3.SecretsManager 1.21.34 service generated with mypy-boto3-builder 7.5.8" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = ">=4.1.0" + +[[package]] +name = "mypy-boto3-ssm" +version = "1.21.34" +description = "Type annotations for boto3.SSM 1.21.34 service generated with mypy-boto3-builder 7.5.8" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = ">=4.1.0" + [[package]] name = "mypy-extensions" version = "0.4.3" @@ -987,7 +1020,7 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "4.0.1" +version = "4.1.1" description = "Backported and Experimental Type Hints for Python 3.6+" category = "main" optional = false @@ -1056,7 +1089,7 @@ pydantic = ["pydantic", "email-validator"] [metadata] lock-version = "1.1" python-versions = "^3.6.2" -content-hash = "57aafebc2e76e8f509411d29a8ccdf8c6c2eb0b17923a26665be7ff2cd658af7" +content-hash = "53ae8d240fed3828519fc3cac85143d07e35d348809eca9c554a022da14148ca" [metadata.files] atomicwrites = [ @@ -1262,28 +1295,12 @@ markdown = [ {file = "Markdown-3.3.5.tar.gz", hash = "sha256:26e9546bfbcde5fcd072bd8f612c9c1b6e2677cb8aadbdf65206674f46dde069"}, ] markupsafe = [ - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, @@ -1292,27 +1309,14 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, @@ -1322,12 +1326,6 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, @@ -1382,6 +1380,18 @@ mypy = [ {file = "mypy-0.931-py3-none-any.whl", hash = "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d"}, {file = "mypy-0.931.tar.gz", hash = "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce"}, ] +mypy-boto3-appconfig = [ + {file = "mypy-boto3-appconfig-1.21.34.tar.gz", hash = "sha256:620350b576e7a5df62b30829f5b9760e4aa105e1b7b2455cccb2be32e529e950"}, + {file = "mypy_boto3_appconfig-1.21.34-py3-none-any.whl", hash = "sha256:1c9c20e5c9436f65989e35692497a3c1c19d6b28aeb9d6989cee3b0cfb1b3f7d"}, +] +mypy-boto3-secretsmanager = [ + {file = "mypy-boto3-secretsmanager-1.21.34.tar.gz", hash = "sha256:91f517ef0b3e1fb668db873146b8404fd6926c300ba0cef8df847e8929b4b4e5"}, + {file = "mypy_boto3_secretsmanager-1.21.34-py3-none-any.whl", hash = "sha256:534fed05d1a5b5caf7032a88f295d58addf1135905d1559a87109a41e34b3b23"}, +] +mypy-boto3-ssm = [ + {file = "mypy-boto3-ssm-1.21.34.tar.gz", hash = "sha256:cfc59e6c0513a3b72e3036a1785fcb57a1c4563db0e05515b0f082a34e0981c3"}, + {file = "mypy_boto3_ssm-1.21.34-py3-none-any.whl", hash = "sha256:f799b77d611c4d84d2915957423d06de8ee0c270d385374387c7b022f5941b34"}, +] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, @@ -1626,8 +1636,8 @@ typed-ast = [ {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typing-extensions = [ - {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, - {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, + {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, + {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, ] urllib3 = [ {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, diff --git a/pyproject.toml b/pyproject.toml index 1f95a3427e5..a5bb126af29 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,6 @@ boto3 = "^1.18" jmespath = "^0.10.0" pydantic = {version = "^1.8.2", optional = true } email-validator = {version = "*", optional = true } -boto3-stubs = {extras = ["appconfig", "ssm", "secretsmanager"], version = "^1.21.37"} [tool.poetry.dev-dependencies] coverage = {extras = ["toml"], version = "^6.2"} @@ -55,6 +54,9 @@ mkdocs-git-revision-date-plugin = "^0.3.1" mike = "^0.6.0" mypy = "^0.931" mkdocs-material = "^8.2.4" +mypy-boto3-appconfig = "^1.21.34" +mypy-boto3-ssm = "^1.21.34" +mypy-boto3-secretsmanager = "^1.21.34" [tool.poetry.extras] From cd71ee0839c0bec45bfc903863c4210783e5e232 Mon Sep 17 00:00:00 2001 From: Ran Isenberg Date: Tue, 19 Apr 2022 20:29:38 +0300 Subject: [PATCH 07/18] cr fix --- aws_lambda_powertools/utilities/parameters/appconfig.py | 2 +- aws_lambda_powertools/utilities/parameters/secrets.py | 2 +- aws_lambda_powertools/utilities/parameters/ssm.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index 37f7ef3b3c3..4ab79f47667 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -84,7 +84,7 @@ def __init__( self.client = boto3_client else: session = boto3_session or boto3.session.Session() - self.client: AppConfigClient = session.client("appconfig", config=config) + self.client: "AppConfigClient" = session.client("appconfig", config=config) self.application = resolve_env_var_choice( choice=application, env=os.getenv(constants.SERVICE_NAME_ENV, "service_undefined") diff --git a/aws_lambda_powertools/utilities/parameters/secrets.py b/aws_lambda_powertools/utilities/parameters/secrets.py index a084e53935b..e0775b7a0dd 100644 --- a/aws_lambda_powertools/utilities/parameters/secrets.py +++ b/aws_lambda_powertools/utilities/parameters/secrets.py @@ -80,7 +80,7 @@ def __init__( self.client = boto3_client else: session = boto3_session or boto3.session.Session() - self.client: SecretsManagerClient = session.client("secretsmanager", config=config) + self.client: "SecretsManagerClient" = session.client("secretsmanager", config=config) super().__init__() diff --git a/aws_lambda_powertools/utilities/parameters/ssm.py b/aws_lambda_powertools/utilities/parameters/ssm.py index 72a87c37936..7ae8dd0e59e 100644 --- a/aws_lambda_powertools/utilities/parameters/ssm.py +++ b/aws_lambda_powertools/utilities/parameters/ssm.py @@ -96,7 +96,7 @@ def __init__( self.client = boto3_client else: session = boto3_session or boto3.session.Session() - self.client: SSMClient = session.client("ssm", config=config) + self.client: "SSMClient" = session.client("ssm", config=config) super().__init__() From 9b99f0b3e84c28ef6bbe6919bda6aefb10967c2c Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Wed, 18 May 2022 15:09:46 +0200 Subject: [PATCH 08/18] chore: encapsulate client building logic in staticmethod --- .../utilities/parameters/appconfig.py | 11 +++-- .../utilities/parameters/base.py | 44 ++++++++++++++++++- .../utilities/parameters/secrets.py | 11 ++--- .../utilities/parameters/ssm.py | 11 ++--- 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index 4ab79f47667..844500aa79d 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -79,12 +79,11 @@ def __init__( Initialize the App Config client """ - config = config or Config() - if boto3_client is not None: - self.client = boto3_client - else: - session = boto3_session or boto3.session.Session() - self.client: "AppConfigClient" = session.client("appconfig", config=config) + super().__init__() + + self.client: "AppConfigClient" = self._build_boto3_client( + service_name="appconfig", client=boto3_client, session=boto3_session, config=config + ) self.application = resolve_env_var_choice( choice=application, env=os.getenv(constants.SERVICE_NAME_ENV, "service_undefined") diff --git a/aws_lambda_powertools/utilities/parameters/base.py b/aws_lambda_powertools/utilities/parameters/base.py index 9c6e74ffb00..a4cd47892a1 100644 --- a/aws_lambda_powertools/utilities/parameters/base.py +++ b/aws_lambda_powertools/utilities/parameters/base.py @@ -7,10 +7,19 @@ from abc import ABC, abstractmethod from collections import namedtuple from datetime import datetime, timedelta -from typing import Any, Dict, Optional, Tuple, Union +from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Type, Union + +import boto3 +from botocore.config import Config from .exceptions import GetParameterError, TransformParameterError +if TYPE_CHECKING: + from mypy_boto3_appconfig import AppConfigClient + from mypy_boto3_secretsmanager import SecretsManagerClient + from mypy_boto3_ssm import SSMClient + + DEFAULT_MAX_AGE_SECS = 5 ExpirableValue = namedtuple("ExpirableValue", ["value", "ttl"]) # These providers will be dynamically initialized on first use of the helper functions @@ -18,6 +27,7 @@ TRANSFORM_METHOD_JSON = "json" TRANSFORM_METHOD_BINARY = "binary" SUPPORTED_TRANSFORM_METHODS = [TRANSFORM_METHOD_JSON, TRANSFORM_METHOD_BINARY] +ParameterClients = Union["AppConfigClient", "SecretsManagerClient", "SSMClient"] class BaseProvider(ABC): @@ -180,6 +190,38 @@ def _get_multiple(self, path: str, **sdk_options) -> Dict[str, str]: def clear_cache(self): self.store.clear() + @staticmethod + def _build_boto3_client( + service_name: str, + client: Optional[ParameterClients] = None, + session: Optional[Type[boto3.Session]] = None, + config: Optional[Type[Config]] = None, + ) -> Type[ParameterClients]: + """Builds a low level boto3 client with session and config provided + + Parameters + ---------- + service_name : str + AWS service name to instantiate a boto3 client, e.g. ssm + client : Optional[ParameterClients], optional + boto3 client instance, by default None + session : Optional[Type[boto3.Session]], optional + boto3 session instance, by default None + config : Optional[Type[Config]], optional + botocore config instance to configure client, by default None + + Returns + ------- + Type[ParameterClients] + Instance of a boto3 client for Parameters feature (e.g., ssm, appconfig, secretsmanager, etc.) + """ + if client is not None: + return client + + session = session or boto3.Session() + config = config or Config() + return session.client(service_name=service_name, config=config) + def get_transform_method(key: str, transform: Optional[str] = None) -> Optional[str]: """ diff --git a/aws_lambda_powertools/utilities/parameters/secrets.py b/aws_lambda_powertools/utilities/parameters/secrets.py index e0775b7a0dd..b89ee92187a 100644 --- a/aws_lambda_powertools/utilities/parameters/secrets.py +++ b/aws_lambda_powertools/utilities/parameters/secrets.py @@ -75,15 +75,12 @@ def __init__( Initialize the Secrets Manager client """ - config = config or Config() - if boto3_client is not None: - self.client = boto3_client - else: - session = boto3_session or boto3.session.Session() - self.client: "SecretsManagerClient" = session.client("secretsmanager", config=config) - super().__init__() + self.client: "SecretsManagerClient" = self._build_boto3_client( + service_name="secretsmanager", client=boto3_client, session=boto3_session, config=config + ) + def _get(self, name: str, **sdk_options) -> str: """ Retrieve a parameter value from AWS Systems Manager Parameter Store diff --git a/aws_lambda_powertools/utilities/parameters/ssm.py b/aws_lambda_powertools/utilities/parameters/ssm.py index 7ae8dd0e59e..aa6045f4b0f 100644 --- a/aws_lambda_powertools/utilities/parameters/ssm.py +++ b/aws_lambda_powertools/utilities/parameters/ssm.py @@ -91,15 +91,12 @@ def __init__( Initialize the SSM Parameter Store client """ - config = config or Config() - if boto3_client is not None: - self.client = boto3_client - else: - session = boto3_session or boto3.session.Session() - self.client: "SSMClient" = session.client("ssm", config=config) - super().__init__() + self.client: "SSMClient" = self._build_boto3_client( + service_name="ssm", client=boto3_client, session=boto3_session, config=config + ) + # We break Liskov substitution principle due to differences in signatures of this method and superclass get method # We ignore mypy error, as changes to the signature here or in a superclass is a breaking change to users def get( # type: ignore[override] From d2c8f30562daab9fa3feab700031e0b2489df164 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Wed, 18 May 2022 15:37:23 +0200 Subject: [PATCH 09/18] feat(parameters): add boto3_client for dynamodb resource client --- .../utilities/parameters/base.py | 35 +++++++++++++++++++ .../utilities/parameters/dynamodb.py | 23 ++++++++---- poetry.lock | 18 ++++++++-- pyproject.toml | 1 + 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/base.py b/aws_lambda_powertools/utilities/parameters/base.py index a4cd47892a1..d44e468d715 100644 --- a/aws_lambda_powertools/utilities/parameters/base.py +++ b/aws_lambda_powertools/utilities/parameters/base.py @@ -16,6 +16,7 @@ if TYPE_CHECKING: from mypy_boto3_appconfig import AppConfigClient + from mypy_boto3_dynamodb import DynamoDBServiceResource from mypy_boto3_secretsmanager import SecretsManagerClient from mypy_boto3_ssm import SSMClient @@ -28,6 +29,7 @@ TRANSFORM_METHOD_BINARY = "binary" SUPPORTED_TRANSFORM_METHODS = [TRANSFORM_METHOD_JSON, TRANSFORM_METHOD_BINARY] ParameterClients = Union["AppConfigClient", "SecretsManagerClient", "SSMClient"] +ParameterResourceClients = Union["DynamoDBServiceResource"] class BaseProvider(ABC): @@ -222,6 +224,39 @@ def _build_boto3_client( config = config or Config() return session.client(service_name=service_name, config=config) + @staticmethod + def _build_boto3_resource_client( + service_name: str, + client: Optional[ParameterResourceClients] = None, + session: Optional[Type[boto3.Session]] = None, + config: Optional[Type[Config]] = None, + endpoint_url: Optional[str] = None, + ) -> Type[ParameterResourceClients]: + """Builds a high level boto3 resource client with session, config and endpoint_url provided + + Parameters + ---------- + service_name : str + AWS service name to instantiate a boto3 client, e.g. ssm + client : Optional[ParameterResourceClients], optional + boto3 client instance, by default None + session : Optional[Type[boto3.Session]], optional + boto3 session instance, by default None + config : Optional[Type[Config]], optional + botocore config instance to configure client, by default None + + Returns + ------- + Type[ParameterResourceClients] + Instance of a boto3 client for Parameters feature (e.g., ssm, appconfig, secretsmanager, etc.) + """ + if client is not None: + return client + + session = session or boto3.Session() + config = config or Config() + return session.resource(service_name=service_name, config=config, endpoint_url=endpoint_url) + def get_transform_method(key: str, transform: Optional[str] = None) -> Optional[str]: """ diff --git a/aws_lambda_powertools/utilities/parameters/dynamodb.py b/aws_lambda_powertools/utilities/parameters/dynamodb.py index 9220edf3b05..ea4cf819a74 100644 --- a/aws_lambda_powertools/utilities/parameters/dynamodb.py +++ b/aws_lambda_powertools/utilities/parameters/dynamodb.py @@ -3,7 +3,7 @@ """ -from typing import Dict, Optional +from typing import TYPE_CHECKING, Dict, Optional import boto3 from boto3.dynamodb.conditions import Key @@ -11,6 +11,10 @@ from .base import BaseProvider +if TYPE_CHECKING: + from mypy_boto3_dynamodb import DynamoDBServiceResource + from mypy_boto3_dynamodb.service_resource import Table + class DynamoDBProvider(BaseProvider): """ @@ -32,6 +36,9 @@ class DynamoDBProvider(BaseProvider): Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional Boto3 session to use for AWS API communication + boto3_client: DynamoDBServiceResource, optional + Boto3 DynamoDB Resource Client to use for AWS API communication, + will be used instead of boto3_session if both provided Example ------- @@ -152,15 +159,19 @@ def __init__( endpoint_url: Optional[str] = None, config: Optional[Config] = None, boto3_session: Optional[boto3.session.Session] = None, + boto3_client: Optional["DynamoDBServiceResource"] = None, ): """ Initialize the DynamoDB client """ - - config = config or Config() - session = boto3_session or boto3.session.Session() - - self.table = session.resource("dynamodb", endpoint_url=endpoint_url, config=config).Table(table_name) + self.client: "DynamoDBServiceResource" = self._build_boto3_resource_client( + service_name="dynamodb", + client=boto3_client, + session=boto3_session, + config=config, + endpoint_url=endpoint_url, + ) + self.table: "Table" = self.client.Table(table_name) self.key_attr = key_attr self.sort_attr = sort_attr diff --git a/poetry.lock b/poetry.lock index 759182e900c..ef3694f3ec0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -625,6 +625,17 @@ python-versions = ">=3.6" [package.dependencies] typing-extensions = ">=4.1.0" +[[package]] +name = "mypy-boto3-dynamodb" +version = "1.23.0.post1" +description = "Type annotations for boto3.DynamoDB 1.23.0 service generated with mypy-boto3-builder 7.5.14" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = ">=4.1.0" + [[package]] name = "mypy-boto3-secretsmanager" version = "1.21.34" @@ -1090,7 +1101,7 @@ pydantic = ["pydantic", "email-validator"] [metadata] lock-version = "1.1" python-versions = "^3.6.2" -content-hash = "b730dd1c59fa3030c0280b255f1cbefca5afa4b86b3178fcf014461734e05042" +content-hash = "03b50d43366216805a13a1e258c0dcc5b82e543ada45264ea60ccb8c73cbfe62" [metadata.files] atomicwrites = [ @@ -1387,6 +1398,10 @@ mypy-boto3-appconfig = [ {file = "mypy-boto3-appconfig-1.21.34.tar.gz", hash = "sha256:620350b576e7a5df62b30829f5b9760e4aa105e1b7b2455cccb2be32e529e950"}, {file = "mypy_boto3_appconfig-1.21.34-py3-none-any.whl", hash = "sha256:1c9c20e5c9436f65989e35692497a3c1c19d6b28aeb9d6989cee3b0cfb1b3f7d"}, ] +mypy-boto3-dynamodb = [ + {file = "mypy-boto3-dynamodb-1.23.0.post1.tar.gz", hash = "sha256:4670825645d041881f3f37a70b38e4b771171942808e49a011a63a9ea6cf494c"}, + {file = "mypy_boto3_dynamodb-1.23.0.post1-py3-none-any.whl", hash = "sha256:fed40bd6e987d4dbe2551b2a33106f23965111570e0a84e9e7a3caf65d1c79f9"}, +] mypy-boto3-secretsmanager = [ {file = "mypy-boto3-secretsmanager-1.21.34.tar.gz", hash = "sha256:91f517ef0b3e1fb668db873146b8404fd6926c300ba0cef8df847e8929b4b4e5"}, {file = "mypy_boto3_secretsmanager-1.21.34-py3-none-any.whl", hash = "sha256:534fed05d1a5b5caf7032a88f295d58addf1135905d1559a87109a41e34b3b23"}, @@ -1412,7 +1427,6 @@ pbr = [ {file = "pbr-5.8.0.tar.gz", hash = "sha256:672d8ebee84921862110f23fcec2acea191ef58543d34dfe9ef3d9f13c31cddf"}, ] pdoc3 = [ - {file = "pdoc3-0.10.0-py3-none-any.whl", hash = "sha256:ba45d1ada1bd987427d2bf5cdec30b2631a3ff5fb01f6d0e77648a572ce6028b"}, {file = "pdoc3-0.10.0.tar.gz", hash = "sha256:5f22e7bcb969006738e1aa4219c75a32f34c2d62d46dc9d2fb2d3e0b0287e4b7"}, ] platformdirs = [ diff --git a/pyproject.toml b/pyproject.toml index 622c3180ba5..8c14bfe5bb1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,7 @@ mkdocs-material = "^8.2.7" mypy-boto3-secretsmanager = "^1.21.34" mypy-boto3-ssm = "^1.21.34" mypy-boto3-appconfig = "^1.21.34" +mypy-boto3-dynamodb = "^1.23.0" [tool.poetry.extras] From d854f1c575c23331fd58b716e6b9c0750906bfb1 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Wed, 18 May 2022 15:53:17 +0200 Subject: [PATCH 10/18] chore: shorten docstrings, fix union single typehint --- .../utilities/parameters/appconfig.py | 4 ++-- aws_lambda_powertools/utilities/parameters/base.py | 14 +++++++------- .../utilities/parameters/dynamodb.py | 5 ++--- .../utilities/parameters/secrets.py | 4 ++-- aws_lambda_powertools/utilities/parameters/ssm.py | 4 ++-- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index 844500aa79d..71768d71b38 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -33,9 +33,9 @@ class AppConfigProvider(BaseProvider): config: botocore.config.Config, optional Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional - Boto3 session to use for AWS API communication, will not be used if boto3_client is not None + Boto3 session to create a boto3_client from boto3_client: AppConfigClient, optional - Boto3 Client to use for AWS API communication, will be used instead of boto3_session if both provided + Boto3 AppConfig Client to use, boto3_session will be ignored if both are provided Example ------- diff --git a/aws_lambda_powertools/utilities/parameters/base.py b/aws_lambda_powertools/utilities/parameters/base.py index d44e468d715..4ec73061262 100644 --- a/aws_lambda_powertools/utilities/parameters/base.py +++ b/aws_lambda_powertools/utilities/parameters/base.py @@ -29,7 +29,6 @@ TRANSFORM_METHOD_BINARY = "binary" SUPPORTED_TRANSFORM_METHODS = [TRANSFORM_METHOD_JSON, TRANSFORM_METHOD_BINARY] ParameterClients = Union["AppConfigClient", "SecretsManagerClient", "SSMClient"] -ParameterResourceClients = Union["DynamoDBServiceResource"] class BaseProvider(ABC): @@ -210,7 +209,7 @@ def _build_boto3_client( session : Optional[Type[boto3.Session]], optional boto3 session instance, by default None config : Optional[Type[Config]], optional - botocore config instance to configure client, by default None + botocore config instance to configure client with, by default None Returns ------- @@ -224,21 +223,22 @@ def _build_boto3_client( config = config or Config() return session.client(service_name=service_name, config=config) + # maintenance: change DynamoDBServiceResource type to ParameterResourceClients when we expand @staticmethod def _build_boto3_resource_client( service_name: str, - client: Optional[ParameterResourceClients] = None, + client: Optional["DynamoDBServiceResource"] = None, session: Optional[Type[boto3.Session]] = None, config: Optional[Type[Config]] = None, endpoint_url: Optional[str] = None, - ) -> Type[ParameterResourceClients]: + ) -> Type["DynamoDBServiceResource"]: """Builds a high level boto3 resource client with session, config and endpoint_url provided Parameters ---------- service_name : str AWS service name to instantiate a boto3 client, e.g. ssm - client : Optional[ParameterResourceClients], optional + client : Optional[DynamoDBServiceResource], optional boto3 client instance, by default None session : Optional[Type[boto3.Session]], optional boto3 session instance, by default None @@ -247,8 +247,8 @@ def _build_boto3_resource_client( Returns ------- - Type[ParameterResourceClients] - Instance of a boto3 client for Parameters feature (e.g., ssm, appconfig, secretsmanager, etc.) + Type[DynamoDBServiceResource] + Instance of a boto3 resource client for Parameters feature (e.g., dynamodb, etc.) """ if client is not None: return client diff --git a/aws_lambda_powertools/utilities/parameters/dynamodb.py b/aws_lambda_powertools/utilities/parameters/dynamodb.py index ea4cf819a74..0dbf62f1d6c 100644 --- a/aws_lambda_powertools/utilities/parameters/dynamodb.py +++ b/aws_lambda_powertools/utilities/parameters/dynamodb.py @@ -35,10 +35,9 @@ class DynamoDBProvider(BaseProvider): config: botocore.config.Config, optional Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional - Boto3 session to use for AWS API communication + Boto3 session to create a boto3_client from boto3_client: DynamoDBServiceResource, optional - Boto3 DynamoDB Resource Client to use for AWS API communication, - will be used instead of boto3_session if both provided + Boto3 DynamoDB Resource Client to use; boto3_session will be ignored if both are provided Example ------- diff --git a/aws_lambda_powertools/utilities/parameters/secrets.py b/aws_lambda_powertools/utilities/parameters/secrets.py index b89ee92187a..affdaf2e4dd 100644 --- a/aws_lambda_powertools/utilities/parameters/secrets.py +++ b/aws_lambda_powertools/utilities/parameters/secrets.py @@ -23,9 +23,9 @@ class SecretsProvider(BaseProvider): config: botocore.config.Config, optional Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional - Boto3 session to use for AWS API communication, will not be used if boto3_client is not None + Boto3 session to create a boto3_client from boto3_client: SecretsManagerClient, optional - Boto3 Client to use for AWS API communication, will be used instead of boto3_session if both provided + Boto3 SecretsManager Client to use, boto3_session will be ignored if both are provided Example ------- diff --git a/aws_lambda_powertools/utilities/parameters/ssm.py b/aws_lambda_powertools/utilities/parameters/ssm.py index aa6045f4b0f..3b3e782fd45 100644 --- a/aws_lambda_powertools/utilities/parameters/ssm.py +++ b/aws_lambda_powertools/utilities/parameters/ssm.py @@ -23,9 +23,9 @@ class SSMProvider(BaseProvider): config: botocore.config.Config, optional Botocore configuration to pass during client initialization boto3_session : boto3.session.Session, optional - Boto3 session to use for AWS API communication, will not be used if boto3_client is not None + Boto3 session to create a boto3_client from boto3_client: SSMClient, optional - Boto3 Client to use for AWS API communication, will be used instead of boto3_session if both provided + Boto3 SSM Client to use, boto3_session will be ignored if both are provided Example ------- From 63bdde6f47f5122ac8625fbecde725e4098bda4a Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Wed, 18 May 2022 16:12:11 +0200 Subject: [PATCH 11/18] chore: ignore mypy ret-type/misc caught by new typed client --- aws_lambda_powertools/utilities/parameters/base.py | 2 +- aws_lambda_powertools/utilities/parameters/dynamodb.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/base.py b/aws_lambda_powertools/utilities/parameters/base.py index 4ec73061262..ce03b757618 100644 --- a/aws_lambda_powertools/utilities/parameters/base.py +++ b/aws_lambda_powertools/utilities/parameters/base.py @@ -231,7 +231,7 @@ def _build_boto3_resource_client( session: Optional[Type[boto3.Session]] = None, config: Optional[Type[Config]] = None, endpoint_url: Optional[str] = None, - ) -> Type["DynamoDBServiceResource"]: + ) -> "DynamoDBServiceResource": """Builds a high level boto3 resource client with session, config and endpoint_url provided Parameters diff --git a/aws_lambda_powertools/utilities/parameters/dynamodb.py b/aws_lambda_powertools/utilities/parameters/dynamodb.py index 0dbf62f1d6c..fee09035d7f 100644 --- a/aws_lambda_powertools/utilities/parameters/dynamodb.py +++ b/aws_lambda_powertools/utilities/parameters/dynamodb.py @@ -193,7 +193,9 @@ def _get(self, name: str, **sdk_options) -> str: # Explicit arguments will take precedence over keyword arguments sdk_options["Key"] = {self.key_attr: name} - return self.table.get_item(**sdk_options)["Item"][self.value_attr] + # maintenance: look for better ways to correctly type DynamoDB multiple return types + # without a breaking change within ABC return type + return self.table.get_item(**sdk_options)["Item"][self.value_attr] # type: ignore[return-value] def _get_multiple(self, path: str, **sdk_options) -> Dict[str, str]: """ @@ -219,4 +221,6 @@ def _get_multiple(self, path: str, **sdk_options) -> Dict[str, str]: response = self.table.query(**sdk_options) items.extend(response.get("Items", [])) - return {item[self.sort_attr]: item[self.value_attr] for item in items} + # maintenance: look for better ways to correctly type DynamoDB multiple return types + # without a breaking change within ABC return type + return {item[self.sort_attr]: item[self.value_attr] for item in items} # type: ignore[misc] From 3528b8eb54beea64effcad992f08bda90ded1e2d Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Wed, 18 May 2022 16:19:39 +0200 Subject: [PATCH 12/18] docs: add note on boto3_client usefulness for searching --- docs/utilities/parameters.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 5c067a181a4..1845123809d 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -487,6 +487,8 @@ The **`config`** , **`boto3_session`**, and **`boto3_client`** parameters enabl ???+ tip You can use a custom session for retrieving parameters cross-account/region and for snapshot testing. + When using VPC private endpoints, you can pass a custom client altogether. It's also useful for testing when injecting fake instances. + === "Custom session" ```python hl_lines="2 4 5" From 579f8e1efdb31d6337ebcdec2b0583ec0ad7ccfa Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Wed, 18 May 2022 16:39:53 +0200 Subject: [PATCH 13/18] chore: tests for dynamodb custom boto3 client --- tests/functional/test_utilities_parameters.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/functional/test_utilities_parameters.py b/tests/functional/test_utilities_parameters.py index 7ba7c507490..8e3f34a3115 100644 --- a/tests/functional/test_utilities_parameters.py +++ b/tests/functional/test_utilities_parameters.py @@ -174,6 +174,35 @@ def test_dynamodb_provider_get_sdk_options(mock_name, mock_value, config): stubber.deactivate() +def test_dynamodb_provider_get_with_custom_client(mock_name, mock_value): + """ + Test DynamoDBProvider.get() with SDK options + """ + + table_name = "TEST_TABLE" + client = boto3.resource("dynamodb") + # Create a new provider + provider = parameters.DynamoDBProvider(table_name, boto3_client=client) + + # Stub the boto3 client + stubber = stub.Stubber(provider.table.meta.client) + response = {"Item": {"id": {"S": mock_name}, "value": {"S": mock_value}}} + expected_params = {"TableName": table_name, "Key": {"id": mock_name}, "ConsistentRead": True} + stubber.add_response("get_item", response, expected_params) + stubber.activate() + + try: + value = provider.get(mock_name, ConsistentRead=True) + + assert value == mock_value + # table meta client will spun up from provider.client provided + # hence having a different hash + assert id(client) == id(provider.client) + stubber.assert_no_pending_responses() + finally: + stubber.deactivate() + + def test_dynamodb_provider_get_sdk_options_overwrite(mock_name, mock_value, config): """ Test DynamoDBProvider.get() with SDK options that should be overwritten From a8350ae315145380a1bc74e49f2b00c201adb2d0 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Wed, 18 May 2022 16:42:44 +0200 Subject: [PATCH 14/18] chore: skip test coverage at runtime type_checking --- pyproject.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8c14bfe5bb1..93ae802d6c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,8 +91,11 @@ exclude_lines = [ "if 0:", "if __name__ == .__main__.:", - # Ignore type function overload - "@overload", + # Ignore runtime type checking + "if TYPE_CHECKING:", + + # Ignore type function overload + "@overload", ] [tool.isort] From cec2f33d30c226affe28dc10e7c97584c340293f Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Wed, 18 May 2022 16:47:53 +0200 Subject: [PATCH 15/18] chore: re-add boto config fixture custom client test --- tests/functional/test_utilities_parameters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_utilities_parameters.py b/tests/functional/test_utilities_parameters.py index 8e3f34a3115..7b62b232008 100644 --- a/tests/functional/test_utilities_parameters.py +++ b/tests/functional/test_utilities_parameters.py @@ -174,13 +174,13 @@ def test_dynamodb_provider_get_sdk_options(mock_name, mock_value, config): stubber.deactivate() -def test_dynamodb_provider_get_with_custom_client(mock_name, mock_value): +def test_dynamodb_provider_get_with_custom_client(mock_name, mock_value, config): """ Test DynamoDBProvider.get() with SDK options """ table_name = "TEST_TABLE" - client = boto3.resource("dynamodb") + client = boto3.resource("dynamodb", config=config) # Create a new provider provider = parameters.DynamoDBProvider(table_name, boto3_client=client) From 19ec9ae2f78ea4f97e96eb0f9b1eb4ac3ddfe674 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Thu, 19 May 2022 08:34:07 +0200 Subject: [PATCH 16/18] chore: remove super leftover --- aws_lambda_powertools/utilities/parameters/appconfig.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/appconfig.py b/aws_lambda_powertools/utilities/parameters/appconfig.py index 71768d71b38..380e355d673 100644 --- a/aws_lambda_powertools/utilities/parameters/appconfig.py +++ b/aws_lambda_powertools/utilities/parameters/appconfig.py @@ -91,8 +91,6 @@ def __init__( self.environment = environment self.current_version = "" - super().__init__() - def _get(self, name: str, **sdk_options) -> str: """ Retrieve a parameter value from AWS App config. From 58e2d5e7bcf869a1dfe81b0407ec86d2514aa1e0 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Thu, 19 May 2022 18:17:32 +0200 Subject: [PATCH 17/18] chore: final adjustments --- .../utilities/parameters/dynamodb.py | 5 +-- docs/utilities/parameters.md | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/aws_lambda_powertools/utilities/parameters/dynamodb.py b/aws_lambda_powertools/utilities/parameters/dynamodb.py index fee09035d7f..612ddf827d3 100644 --- a/aws_lambda_powertools/utilities/parameters/dynamodb.py +++ b/aws_lambda_powertools/utilities/parameters/dynamodb.py @@ -163,14 +163,13 @@ def __init__( """ Initialize the DynamoDB client """ - self.client: "DynamoDBServiceResource" = self._build_boto3_resource_client( + self.table: "Table" = self._build_boto3_resource_client( service_name="dynamodb", client=boto3_client, session=boto3_session, config=config, endpoint_url=endpoint_url, - ) - self.table: "Table" = self.client.Table(table_name) + ).Table(table_name) self.key_attr = key_attr self.sort_attr = sort_attr diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 1845123809d..36990fdd2cb 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -480,6 +480,44 @@ Here is the mapping between this utility's functions and methods and the underly | App Config | `get_app_config` | `appconfig` | [get_configuration](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appconfig.html#AppConfig.Client.get_configuration) | +### Bring your own boto client + +You can use `boto3_client` parameter via any of the available [Provider Classes](#built-in-provider-class). Some providers expect a low level boto3 client while others expect a high level boto3 client, here is the mapping for each of them: + +| Provider | Type | Boto client construction | +| --------------------------------------- | ---------- | ---------------------------- | +| [SSMProvider](#ssmprovider) | low level | `boto3.client("ssm")` | +| [SecretsProvider](#secretsprovider) | low level | `boto3.client("secrets")` | +| [AppConfigProvider](#appconfigprovider) | low level | `boto3.client("appconfig")` | +| [DynamoDBProvider](#dynamodbprovider) | high level | `boto3.resource("dynamodb")` | + + +Bringing them together in a single code snippet would look like this: + +```python title="Example: passing a custom boto3 client for each provider" +import boto3 +from botocore.config import Config + +from aws_lambda_powertools.utilities import parameters + +config = Config(region_name="us-west-1") + +# construct boto clients with any custom configuration +ssm = boto3.client("ssm", config=config) +secrets = boto3.client("secrets", config=config) +appconfig = boto3.client("appconfig", config=config) +dynamodb = boto3.resource("dynamodb", config=config) + +ssm_provider = parameters.SSMProvider(boto3_client=ssm) +secrets_provider = parameters.SecretsProvider(boto3_client=secrets) +appconf_provider = parameters.AppConfigProvider(boto3_client=appconfig, environment="my_env", application="my_app") +dynamodb_provider = parameters.DynamoDBProvider(boto3_client=dynamodb, table_name="my-table") + +``` + +???+ question "When is this useful?" + Injecting a custom boto3 client can make unit/snapshot testing easier, including SDK customizations. + ### Customizing boto configuration The **`config`** , **`boto3_session`**, and **`boto3_client`** parameters enable you to pass in a custom [botocore config object](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html) , [boto3 session](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html), or a [boto3 client](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/boto3.html) when constructing any of the built-in provider classes. From 98dc5397f29c5d62b11c986ae46d316fd0797a55 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Thu, 19 May 2022 18:24:08 +0200 Subject: [PATCH 18/18] chore: correct leftover table client --- tests/functional/test_utilities_parameters.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/functional/test_utilities_parameters.py b/tests/functional/test_utilities_parameters.py index 7b62b232008..2b8291db47b 100644 --- a/tests/functional/test_utilities_parameters.py +++ b/tests/functional/test_utilities_parameters.py @@ -181,6 +181,8 @@ def test_dynamodb_provider_get_with_custom_client(mock_name, mock_value, config) table_name = "TEST_TABLE" client = boto3.resource("dynamodb", config=config) + table_resource_client = client.Table(table_name) + # Create a new provider provider = parameters.DynamoDBProvider(table_name, boto3_client=client) @@ -195,9 +197,8 @@ def test_dynamodb_provider_get_with_custom_client(mock_name, mock_value, config) value = provider.get(mock_name, ConsistentRead=True) assert value == mock_value - # table meta client will spun up from provider.client provided - # hence having a different hash - assert id(client) == id(provider.client) + # confirm table resource client comes from the same custom client provided + assert id(table_resource_client.meta.client) == id(provider.table.meta.client) stubber.assert_no_pending_responses() finally: stubber.deactivate()