diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b717c2f868..f2211ffd9c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,10 +20,10 @@ jobs: os: - ubuntu-latest python: - - "3.8" - - "3.9" - "3.10" - "3.11" + - "3.12" + - "3.13" steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 diff --git a/DEVELOPMENT_GUIDE.md b/DEVELOPMENT_GUIDE.md index 10fcec056f..f7ed56511e 100644 --- a/DEVELOPMENT_GUIDE.md +++ b/DEVELOPMENT_GUIDE.md @@ -26,7 +26,7 @@ Environment setup ----------------- ### 1. Install Python versions -Our officially supported Python versions are 3.8, 3.9 and 3.10. +Our officially supported Python versions are 3.10, 3.11, 3.12, 3.13 and 3.14. Our CI/CD pipeline is setup to run unit tests against Python 3 versions. Make sure you test it before sending a Pull Request. See [Unit testing with multiple Python versions](#unit-testing-with-multiple-python-versions). @@ -40,11 +40,13 @@ easily setup multiple Python versions. For 1. Install PyEnv - `curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash` 1. Restart shell so the path changes take effect - `exec $SHELL` -1. `pyenv install 3.8.16` -1. `pyenv install 3.9.16` -1. `pyenv install 3.10.9` +1. `pyenv install 3.10.20` +1. `pyenv install 3.11.15` +1. `pyenv install 3.12.13` +1. `pyenv install 3.13.12` +1. `pyenv install 3.14.3` 3. Make Python versions available in the project: - `pyenv local 3.8.16 3.9.16 3.10.9` + `pyenv local 3.10.20 3.11.15 3.12.13 3.13.12 3.14.3` Note: also make sure the following lines were written into your `.bashrc` (or `.zshrc`, depending on which shell you are using): ``` @@ -65,7 +67,7 @@ can be found [here](https://black.readthedocs.io/en/stable/integrations/editors. Since black is installed in virtualenv, when you follow [this instruction](https://black.readthedocs.io/en/stable/integrations/editors.html), `which black` might give you this ```bash -(sam38) $ where black +(sam310) $ where black /Users//.pyenv/shims/black ``` @@ -76,11 +78,11 @@ and this will happen: pyenv: black: command not found The `black' command exists in these Python versions: - 3.8.16/envs/sam38 - sam38 + 3.10.16/envs/sam310 + sam310 ``` -A simple workaround is to use `/Users//.pyenv/versions/sam38/bin/black` +A simple workaround is to use `/Users//.pyenv/versions/sam310/bin/black` instead of `/Users//.pyenv/shims/black`. #### Pre-commit @@ -98,15 +100,15 @@ handy plugin that can create virtualenv. Depending on the python version, the following commands would change to be the appropriate python version. -1. Create Virtualenv `sam38` for Python3.8: `pyenv virtualenv 3.8.16 sam38` -1. Activate Virtualenv: `pyenv activate sam38` +1. Create Virtualenv `sam310` for Python3.10: `pyenv virtualenv 3.10.16 sam310` +1. Activate Virtualenv: `pyenv activate sam310` ### 4. Install dev version of SAM transform We will install a development version of SAM transform from source into the virtualenv. -1. Activate Virtualenv: `pyenv activate sam38` +1. Activate Virtualenv: `pyenv activate sam310` 1. Install dev version of SAM transform: `make init` Running tests @@ -120,10 +122,10 @@ Run `make test` or `make test-fast`. Once all tests pass make sure to run ### Unit testing with multiple Python versions -Currently, our officially supported Python versions are 3.8, 3.9 and 3.10. For the most -part, code that works in Python3.8 will work in Pythons 3.9 and 3.10. You only run into problems if you are -trying to use features released in a higher version (for example features introduced into Python3.10 -will not work in Python3.9). If you want to test in many versions, you can create a virtualenv for +Currently, our officially supported Python versions are 3.10, 3.11, 3.12, 3.13 and 3.14. For the most +part, code that works in Python 3.10 will work in later versions. You only run into problems if you are +trying to use features released in a higher version (for example features introduced into Python 3.13 +will not work in Python 3.12). If you want to test in many versions, you can create a virtualenv for each version and flip between them (sourcing the activate script). Typically, we run all tests in one python version locally and then have our ci (appveyor) run all supported versions. diff --git a/bin/_file_formatter.py b/bin/_file_formatter.py index 7e092b234c..94c7401a68 100644 --- a/bin/_file_formatter.py +++ b/bin/_file_formatter.py @@ -5,7 +5,6 @@ import sys from abc import ABC, abstractmethod from pathlib import Path -from typing import Type class FileFormatter(ABC): @@ -34,7 +33,7 @@ def format_str(self, input_str: str) -> str: @staticmethod @abstractmethod - def decode_exception() -> Type[Exception]: + def decode_exception() -> type[Exception]: """Return the exception class when the file content cannot be decoded.""" @staticmethod diff --git a/bin/add_transform_test.py b/bin/add_transform_test.py index ec85b53c97..a8057098bc 100755 --- a/bin/add_transform_test.py +++ b/bin/add_transform_test.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """Automatically create transform tests input and output files given an input template.""" + import argparse import json import shutil @@ -7,7 +8,7 @@ import sys from copy import deepcopy from pathlib import Path -from typing import Any, Dict +from typing import Any from unittest.mock import patch import boto3 @@ -31,12 +32,12 @@ CLI_OPTIONS = parser.parse_args() -def read_json_file(file_path: Path) -> Dict[str, Any]: - template: Dict[str, Any] = json.loads(file_path.read_text(encoding="utf-8")) +def read_json_file(file_path: Path) -> dict[str, Any]: + template: dict[str, Any] = json.loads(file_path.read_text(encoding="utf-8")) return template -def write_json_file(obj: Dict[str, Any], file_path: Path) -> None: +def write_json_file(obj: dict[str, Any], file_path: Path) -> None: with file_path.open("w", encoding="utf-8") as f: json.dump(obj, f, indent=2, sort_keys=True) @@ -54,8 +55,9 @@ def generate_transform_test_output_files(input_file_path: Path, file_basename: s } for _, (region, output_path) in transform_test_output_paths.items(): - with patch("samtranslator.translator.arn_generator._get_region_from_session", return_value=region), patch( - "boto3.session.Session.region_name", region + with ( + patch("samtranslator.translator.arn_generator._get_region_from_session", return_value=region), + patch("boto3.session.Session.region_name", region), ): # Implicit API Plugin may alter input template file, thus passing a copy here. output_fragment = transform(deepcopy(manifest), {}, ManagedPolicyLoader(iam_client)) diff --git a/bin/json-format.py b/bin/json-format.py index c2ae78729f..fdea54084b 100755 --- a/bin/json-format.py +++ b/bin/json-format.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """JSON file formatter (without prettier).""" + import sys from pathlib import Path @@ -7,7 +8,6 @@ sys.path.insert(0, str(Path(__file__).absolute().parent.parent)) import json -from typing import Type from bin._file_formatter import FileFormatter @@ -23,7 +23,7 @@ def format_str(self, input_str: str) -> str: return json.dumps(obj, indent=2, sort_keys=True) + "\n" @staticmethod - def decode_exception() -> Type[Exception]: + def decode_exception() -> type[Exception]: return json.JSONDecodeError @staticmethod diff --git a/bin/parse_cdk_cfn_docs.py b/bin/parse_cdk_cfn_docs.py index 27f6caa610..03d94d0c7c 100755 --- a/bin/parse_cdk_cfn_docs.py +++ b/bin/parse_cdk_cfn_docs.py @@ -11,13 +11,13 @@ import json import sys -from typing import Any, Dict +from typing import Any def main() -> None: obj = json.load(sys.stdin) - out: Dict[str, Any] = {"properties": {}} + out: dict[str, Any] = {"properties": {}} for k, v in obj["Types"].items(): kk = k.replace(".", " ") vv = v["properties"] diff --git a/bin/public_interface.py b/bin/public_interface.py index 7c47889396..fa20f681c4 100755 --- a/bin/public_interface.py +++ b/bin/public_interface.py @@ -7,6 +7,7 @@ (see https://peps.python.org/pep-0008/#descriptive-naming-styles) This CLI tool helps automate the detection of compatibility-breaking changes. """ + import argparse import ast import importlib @@ -17,17 +18,17 @@ import string import sys from pathlib import Path -from typing import Any, Dict, List, NamedTuple, Optional, Set, Union +from typing import Any, NamedTuple, Union _ARGUMENT_SELF = {"kind": "POSITIONAL_OR_KEYWORD", "name": "self"} _PRINTABLE_CHARS = set(string.printable) class InterfaceScanner: - def __init__(self, skipped_modules: Optional[List[str]] = None) -> None: - self.signatures: Dict[str, Union[inspect.Signature]] = {} - self.variables: Set[str] = set() - self.skipped_modules: Set[str] = set(skipped_modules or []) + def __init__(self, skipped_modules: list[str] | None = None) -> None: + self.signatures: dict[str, Union[inspect.Signature]] = {} + self.variables: set[str] = set() + self.skipped_modules: set[str] = set(skipped_modules or []) def scan_interfaces_recursively(self, module_name: str) -> None: if module_name in self.skipped_modules: @@ -63,7 +64,7 @@ def _scan_variables_in_module(self, module_name: str) -> None: else: module_path = module_path.with_suffix(".py") tree = ast.parse("".join([char for char in module_path.read_text() if char in _PRINTABLE_CHARS])) - assignments: List[ast.Assign] = [node for node in ast.iter_child_nodes(tree) if isinstance(node, ast.Assign)] + assignments: list[ast.Assign] = [node for node in ast.iter_child_nodes(tree) if isinstance(node, ast.Assign)] for assignment in assignments: for target in assignment.targets: if not isinstance(target, ast.Name): @@ -97,8 +98,8 @@ def _scan_methods_in_class(self, class_name: str, _class: Any) -> None: self.signatures[full_path] = inspect.signature(method) -def _print(signature: Dict[str, inspect.Signature], variables: Set[str]) -> None: - result: Dict[str, Any] = {"routines": {}, "variables": sorted(variables)} +def _print(signature: dict[str, inspect.Signature], variables: set[str]) -> None: + result: dict[str, Any] = {"routines": {}, "variables": sorted(variables)} for key, value in signature.items(): result["routines"][key] = [ ( @@ -116,23 +117,23 @@ def _print(signature: Dict[str, inspect.Signature], variables: Set[str]) -> None class _BreakingChanges(NamedTuple): - deleted_variables: List[str] - deleted_routines: List[str] - incompatible_routines: List[str] + deleted_variables: list[str] + deleted_routines: list[str] + incompatible_routines: list[str] def is_empty(self) -> bool: return not any([self.deleted_variables, self.deleted_routines, self.incompatible_routines]) @staticmethod - def _argument_to_str(argument: Dict[str, Any]) -> str: + def _argument_to_str(argument: dict[str, Any]) -> str: if "default" in argument: return f'{argument["name"]}={argument["default"]}' return str(argument["name"]) def print_markdown( self, - original_routines: Dict[str, List[Dict[str, Any]]], - routines: Dict[str, List[Dict[str, Any]]], + original_routines: dict[str, list[dict[str, Any]]], + routines: dict[str, list[dict[str, Any]]], ) -> None: """Print all breaking changes in markdown.""" print("\n# Compatibility breaking changes:") @@ -156,7 +157,7 @@ def print_markdown( def _only_new_optional_arguments_or_existing_arguments_optionalized_or_var_arguments( - original_arguments: List[Dict[str, Any]], arguments: List[Dict[str, Any]] + original_arguments: list[dict[str, Any]], arguments: list[dict[str, Any]] ) -> bool: if len(original_arguments) > len(arguments): return False @@ -178,7 +179,7 @@ def _only_new_optional_arguments_or_existing_arguments_optionalized_or_var_argum ) -def _is_compatible(original_arguments: List[Dict[str, Any]], arguments: List[Dict[str, Any]]) -> bool: +def _is_compatible(original_arguments: list[dict[str, Any]], arguments: list[dict[str, Any]]) -> bool: """ If there is an argument change, it is compatible only when - new optional arguments are added or existing arguments become optional. @@ -201,13 +202,13 @@ def _is_compatible(original_arguments: List[Dict[str, Any]], arguments: List[Dic def _detect_breaking_changes( - original_routines: Dict[str, List[Dict[str, Any]]], - original_variables: Set[str], - routines: Dict[str, List[Dict[str, Any]]], - variables: Set[str], + original_routines: dict[str, list[dict[str, Any]]], + original_variables: set[str], + routines: dict[str, list[dict[str, Any]]], + variables: set[str], ) -> _BreakingChanges: - deleted_routines: List[str] = [] - incompatible_routines: List[str] = [] + deleted_routines: list[str] = [] + incompatible_routines: list[str] = [] for routine_path, arguments in original_routines.items(): if routine_path not in routines: deleted_routines.append(routine_path) diff --git a/bin/sam-translate.py b/bin/sam-translate.py index 80903ae8db..3ae9c4a98d 100755 --- a/bin/sam-translate.py +++ b/bin/sam-translate.py @@ -4,6 +4,7 @@ Known limitations: cannot transform CodeUri pointing at local directory. """ + import argparse import json import logging @@ -12,7 +13,6 @@ import sys from functools import reduce from pathlib import Path -from typing import List import boto3 @@ -71,7 +71,7 @@ logging.basicConfig() -def execute_command(command: str, args: List[str]) -> None: +def execute_command(command: str, args: list[str]) -> None: try: aws_cmd = "aws" if platform.system().lower() != "windows" else "aws.cmd" command_with_args = [aws_cmd, "cloudformation", command, *list(args)] diff --git a/bin/transform-test-error-json-format.py b/bin/transform-test-error-json-format.py index 998fa2bf64..1493021ed4 100755 --- a/bin/transform-test-error-json-format.py +++ b/bin/transform-test-error-json-format.py @@ -5,6 +5,7 @@ It makes error json easier to review by breaking down "errorMessage" into list of strings (delimiter: ". "). """ + import sys from pathlib import Path @@ -12,7 +13,7 @@ sys.path.insert(0, str(Path(__file__).absolute().parent.parent)) import json -from typing import Final, Type +from typing import Final from bin._file_formatter import FileFormatter @@ -41,7 +42,7 @@ def format_str(self, input_str: str) -> str: return json.dumps(obj, indent=2, sort_keys=True) + "\n" @staticmethod - def decode_exception() -> Type[Exception]: + def decode_exception() -> type[Exception]: return json.JSONDecodeError @staticmethod diff --git a/bin/yaml-format.py b/bin/yaml-format.py index 4009e4a0e5..c684a2b619 100755 --- a/bin/yaml-format.py +++ b/bin/yaml-format.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """JSON file formatter (without prettier).""" + import sys from pathlib import Path from textwrap import dedent @@ -9,7 +10,7 @@ import re from io import StringIO -from typing import Any, Dict, Type +from typing import Any # We use ruamel.yaml for parsing yaml files because it can preserve comments from ruamel.yaml import YAML @@ -53,7 +54,7 @@ def format_str(self, input_str: str) -> str: return formatted @staticmethod - def _add_test_metadata(obj: Dict[str, Any]) -> None: + def _add_test_metadata(obj: dict[str, Any]) -> None: metadata = obj.get("Metadata", {}) if not metadata: metadata = obj["Metadata"] = {} @@ -63,7 +64,7 @@ def _add_test_metadata(obj: Dict[str, Any]) -> None: metadata["SamTransformTest"] = True @staticmethod - def decode_exception() -> Type[Exception]: + def decode_exception() -> type[Exception]: return YAMLError @staticmethod @@ -75,11 +76,9 @@ def config_additional_args(cls) -> None: cls.arg_parser.add_argument( "--add-test-metadata", action="store_true", - help=dedent( - """\ + help=dedent("""\ Add the testing metadata to yaml file if it doesn't exist: - "Metadata: SamTransformTest: true" """ - ), + "Metadata: SamTransformTest: true" """), ) diff --git a/integration/combination/test_function_with_cwe_dlq_generated.py b/integration/combination/test_function_with_cwe_dlq_generated.py index 34296e205f..4a5bc396f7 100644 --- a/integration/combination/test_function_with_cwe_dlq_generated.py +++ b/integration/combination/test_function_with_cwe_dlq_generated.py @@ -36,7 +36,7 @@ def test_function_with_cwe(self): # checking policy action actions = dlq_policy_statement["Action"] - action_list = actions if isinstance(actions, list) == list else [actions] + action_list = actions if actions is list else [actions] self.assertEqual(len(action_list), 1, "Only one action must be in dead-letter queue policy") self.assertEqual( action_list[0], "sqs:SendMessage", "Action referenced in dead-letter queue policy must be 'sqs:SendMessage'" diff --git a/integration/helpers/deployer/deployer.py b/integration/helpers/deployer/deployer.py index 65bb73595d..94e2c6e123 100644 --- a/integration/helpers/deployer/deployer.py +++ b/integration/helpers/deployer/deployer.py @@ -271,10 +271,8 @@ def wait_for_changeset(self, changeset_id, stack_name): reason = resp.get("StatusReason", "") if ( - status == "FAILED" - and "The submitted information didn't contain changes." in reason - or "No updates are to be performed" in reason - ): + status == "FAILED" and "The submitted information didn't contain changes." in reason + ) or "No updates are to be performed" in reason: raise deploy_exceptions.ChangeEmptyError(stack_name=stack_name) raise deploy_exceptions.ChangeSetError( diff --git a/integration/helpers/deployer/utils/colors.py b/integration/helpers/deployer/utils/colors.py index e97e395b3a..421a16ff65 100644 --- a/integration/helpers/deployer/utils/colors.py +++ b/integration/helpers/deployer/utils/colors.py @@ -3,7 +3,7 @@ This was ported over from the sam-cli repo """ -from typing import Dict, Literal +from typing import Literal SupportedColor = Literal["red", "green", "yellow"] @@ -21,8 +21,8 @@ def cprint(text: str, color: SupportedColor) -> None: class DeployColor: def __init__(self): - self.changeset_color_map: Dict[str, SupportedColor] = {"Add": "green", "Modify": "yellow", "Remove": "red"} - self.status_color_map: Dict[str, SupportedColor] = { + self.changeset_color_map: dict[str, SupportedColor] = {"Add": "green", "Modify": "yellow", "Remove": "red"} + self.status_color_map: dict[str, SupportedColor] = { "CREATE_COMPLETE": "green", "CREATE_FAILED": "red", "CREATE_IN_PROGRESS": "yellow", diff --git a/integration/helpers/resource.py b/integration/helpers/resource.py index 604f317663..56fa7fb304 100644 --- a/integration/helpers/resource.py +++ b/integration/helpers/resource.py @@ -2,8 +2,9 @@ import random import re import string +from collections.abc import Callable, Iterator from pathlib import Path -from typing import Any, Callable, Dict, Iterator, Set +from typing import Any import boto3 from botocore.exceptions import NoRegionError @@ -187,7 +188,7 @@ def current_region_does_not_support(services): return bool(set(services).intersection(set(region_exclude_services["regions"][region]))) -def _resource_using_inline_statemachine_definition(resource: Dict[str, Any]) -> bool: +def _resource_using_inline_statemachine_definition(resource: dict[str, Any]) -> bool: resource_type = resource.get("Type") properties = resource.get("Properties", {}) if resource_type == "AWS::StepFunctions::StateMachine" and properties.get("DefinitionString"): @@ -197,23 +198,23 @@ def _resource_using_inline_statemachine_definition(resource: Dict[str, Any]) -> return False -def _resource_using_s3_events(resource: Dict[str, Any]) -> bool: +def _resource_using_s3_events(resource: dict[str, Any]) -> bool: resource_type = resource.get("Type") properties = resource.get("Properties", {}) return resource_type == "AWS::S3::Bucket" and properties.get("NotificationConfiguration") -def _get_all_event_sources(template_dict: Dict[str, Any]) -> Iterator[Dict[str, Any]]: +def _get_all_event_sources(template_dict: dict[str, Any]) -> Iterator[dict[str, Any]]: resources = template_dict.get("Resources", {}).values() for resource in resources: yield from resource.get("Properties", {}).get("Events", {}).values() -def _event_using_sns_filter_policy_scope(event: Dict[str, Any]) -> bool: +def _event_using_sns_filter_policy_scope(event: dict[str, Any]) -> bool: return event["Type"] == "SNS" and "FilterPolicyScope" in event.get("Properties", {}) -SERVICE_DETECTORS: Dict[str, Callable[[Dict[str, Any], Set[str]], bool]] = { +SERVICE_DETECTORS: dict[str, Callable[[dict[str, Any], set[str]], bool]] = { HTTP_API: lambda template_dict, cfn_resource_types: "AWS::ApiGatewayV2::Api" in cfn_resource_types, REST_API: lambda template_dict, cfn_resource_types: "AWS::ApiGateway::RestApi" in cfn_resource_types, SQS: lambda template_dict, cfn_resource_types: "AWS::SQS::Queue" in cfn_resource_types, @@ -234,7 +235,7 @@ def _event_using_sns_filter_policy_scope(event: Dict[str, Any]) -> bool: } -def detect_services(template_dict: Dict[str, Any], cfn_resource_types: Set[str]): +def detect_services(template_dict: dict[str, Any], cfn_resource_types: set[str]): """ Detect which services are used in the template. diff --git a/integration/ruff.toml b/integration/ruff.toml index 2ee0fd223c..a9998944ed 100644 --- a/integration/ruff.toml +++ b/integration/ruff.toml @@ -1,8 +1,8 @@ # black formatter takes care of the line length -line-length = 999 +line-length = 320 -# Mininal python version we support is 3.8 -target-version = "py38" +# Minimal python version we support is 3.10 +target-version = "py310" # The code quality of tests can be a bit lower compared to samtranslator lint.select = [ @@ -24,4 +24,5 @@ lint.select = [ "**/*.py" = [ "S101", # Use of `assert` detected "PLR", # pylint-refactor + "E721", # Use `is` and `is not` for type comparisons ] diff --git a/pyproject.toml b/pyproject.toml index bb5eabbf7e..2d61ea2ca2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.black] line-length = 120 -target_version = ['py38', 'py39', 'py310'] +target_version = ['py310', 'py311', 'py312', 'py313'] exclude = ''' ( diff --git a/requirements/base.txt b/requirements/base.txt index 9ec209cd3c..732e8aff4a 100755 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,8 +1,6 @@ boto3>=1.34.0,<2.0.0 jsonschema<5,>=4.23 -typing_extensions>=4.4 # 3.8 doesn't have Required, TypeGuard and ParamSpec +typing_extensions>=4.4 # resource validation & schema generation -# we should remove support for Python 3.8 soon (and 3.9), but supporting it for now -pydantic>=2.10.6; python_version>="3.8" -pydantic~=2.12.5; python_version>="3.9" +pydantic~=2.12.5 diff --git a/requirements/dev.txt b/requirements/dev.txt index 437f77af19..50604a40cd 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -4,7 +4,7 @@ pytest-xdist>=2.5,<4 pytest-env>=0.6,<1 pytest-rerunfailures>=9.1,<12 pyyaml~=6.0 -ruff~=0.4.5 +ruff~=0.15.6 # Test requirements pytest>=6.2,<8 @@ -19,7 +19,7 @@ tenacity~=9.0 requests~=2.28 # formatter -black==24.3.0 +black==26.3.1 ruamel.yaml==0.17.21 # It can parse yaml while perserving comments # type check diff --git a/ruff.toml b/ruff.toml index 13e5915347..c542a1a81a 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,8 +1,8 @@ # black formatter takes care of the line length -line-length = 999 +line-length = 320 -# Mininal python version we support is 3.8 -target-version = "py38" +# Minimal python version we support is 3.10 +target-version = "py310" lint.select = [ "E", # pycodestyle @@ -38,6 +38,13 @@ lint.ignore = [ "RUF012", # Logging statement uses f-string "G004", + # `import` should be at the top-level of a file + # Used intentionally to avoid circular imports + "PLC0415", + # Object does not implement `__hash__` method + # Intentional in py27hash_fix.py + "PLW1641", + "PTH208", ] [lint.per-file-ignores] @@ -52,6 +59,10 @@ lint.ignore = [ # print() is allowed in bin/ as they are dev tools. "T201", ] +"samtranslator/internal/schema_source/aws_serverless_function.py" = [ + # E501: Line too long - unavoidable due to large Union type + "E501", +] [lint.pylint] max-args = 6 # We have many functions reaching 6 args diff --git a/samtranslator/feature_toggle/feature_toggle.py b/samtranslator/feature_toggle/feature_toggle.py index 5a51a6fece..5961ea110b 100644 --- a/samtranslator/feature_toggle/feature_toggle.py +++ b/samtranslator/feature_toggle/feature_toggle.py @@ -2,7 +2,7 @@ import logging from abc import ABC, abstractmethod from pathlib import Path -from typing import Any, Dict, Optional, cast +from typing import Any, cast import boto3 from botocore.config import Config @@ -32,9 +32,9 @@ class FeatureToggle: def __init__( self, config_provider: "FeatureToggleConfigProvider", - stage: Optional[str], - account_id: Optional[str], - region: Optional[str], + stage: str | None, + account_id: str | None, + region: str | None, ) -> None: self.feature_config = config_provider.config self.stage = stage @@ -102,7 +102,7 @@ class FeatureToggleConfigProvider(ABC): @property @abstractmethod - def config(self) -> Dict[str, Any]: + def config(self) -> dict[str, Any]: pass @@ -113,7 +113,7 @@ def __init__(self) -> None: FeatureToggleConfigProvider.__init__(self) @property - def config(self) -> Dict[str, Any]: + def config(self) -> dict[str, Any]: return {} @@ -123,10 +123,10 @@ class FeatureToggleLocalConfigProvider(FeatureToggleConfigProvider): def __init__(self, local_config_path: str) -> None: FeatureToggleConfigProvider.__init__(self) config_json = Path(local_config_path).read_text(encoding="utf-8") - self.feature_toggle_config = cast(Dict[str, Any], json.loads(config_json)) + self.feature_toggle_config = cast(dict[str, Any], json.loads(config_json)) @property - def config(self) -> Dict[str, Any]: + def config(self) -> dict[str, Any]: return self.feature_toggle_config @@ -154,7 +154,7 @@ def __init__(self, application_id, environment_id, configuration_profile_id, app ClientId="FeatureToggleAppConfigConfigProvider", ) binary_config_string = response["Content"].read() - self.feature_toggle_config = cast(Dict[str, Any], json.loads(binary_config_string.decode("utf-8"))) + self.feature_toggle_config = cast(dict[str, Any], json.loads(binary_config_string.decode("utf-8"))) LOG.info("Finished loading feature toggle config from AppConfig.") except Exception: LOG.exception("Failed to load config from AppConfig. Using empty config.") @@ -162,5 +162,5 @@ def __init__(self, application_id, environment_id, configuration_profile_id, app self.feature_toggle_config = {} @property - def config(self) -> Dict[str, Any]: + def config(self) -> dict[str, Any]: return self.feature_toggle_config diff --git a/samtranslator/internal/deprecation_control.py b/samtranslator/internal/deprecation_control.py index dfb7702f75..5758de3124 100644 --- a/samtranslator/internal/deprecation_control.py +++ b/samtranslator/internal/deprecation_control.py @@ -11,8 +11,9 @@ """ import warnings +from collections.abc import Callable from functools import wraps -from typing import Callable, Optional, TypeVar +from typing import TypeVar from typing_extensions import ParamSpec @@ -20,14 +21,14 @@ RT = TypeVar("RT") # return type -def _make_message(message: str, replacement: Optional[str]) -> str: +def _make_message(message: str, replacement: str | None) -> str: return f"{message}, please use {replacement}" if replacement else message # TODO: make @deprecated able to decorate a class -def deprecated(replacement: Optional[str] = None) -> Callable[[Callable[PT, RT]], Callable[PT, RT]]: +def deprecated(replacement: str | None = None) -> Callable[[Callable[PT, RT]], Callable[PT, RT]]: """ Mark a function/method as deprecated. diff --git a/samtranslator/internal/intrinsics.py b/samtranslator/internal/intrinsics.py index 50c2fd38ff..76b487ea0d 100644 --- a/samtranslator/internal/intrinsics.py +++ b/samtranslator/internal/intrinsics.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional, Union +from typing import Any, Union from samtranslator.intrinsics.resolver import IntrinsicsResolver from samtranslator.model.exceptions import InvalidResourceException @@ -7,9 +7,9 @@ def resolve_string_parameter_in_resource( logical_id: str, intrinsics_resolver: IntrinsicsResolver, - parameter_value: Optional[Union[str, Dict[str, Any]]], + parameter_value: Union[str, dict[str, Any]] | None, parameter_name: str, -) -> Optional[Union[str, Dict[str, Any]]]: +) -> Union[str, dict[str, Any]] | None: """Try to resolve values in a resource from template parameters.""" if not parameter_value: return parameter_value diff --git a/samtranslator/internal/managed_policies.py b/samtranslator/internal/managed_policies.py index a95971b8d0..29af9c0179 100644 --- a/samtranslator/internal/managed_policies.py +++ b/samtranslator/internal/managed_policies.py @@ -1,10 +1,9 @@ import json from pathlib import Path -from typing import Dict, Optional with (Path(__file__).absolute().parent / "data" / "aws_managed_policies.json").open(encoding="utf-8") as f: - _BUNDLED_MANAGED_POLICIES: Dict[str, Dict[str, str]] = json.load(f) + _BUNDLED_MANAGED_POLICIES: dict[str, dict[str, str]] = json.load(f) -def get_bundled_managed_policy_map(partition: str) -> Optional[Dict[str, str]]: +def get_bundled_managed_policy_map(partition: str) -> dict[str, str] | None: return _BUNDLED_MANAGED_POLICIES.get(partition) diff --git a/samtranslator/internal/model/appsync.py b/samtranslator/internal/model/appsync.py index 45bbf9f3fb..a49ce2bfb1 100644 --- a/samtranslator/internal/model/appsync.py +++ b/samtranslator/internal/model/appsync.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, TypedDict, Union +from typing import Any, TypedDict, Union from typing_extensions import Required @@ -53,7 +53,7 @@ class DeltaSyncConfigType(TypedDict): class DynamoDBConfigType(TypedDict, total=False): - AwsRegion: Union[str, Dict[str, str]] + AwsRegion: Union[str, dict[str, str]] TableName: str UseCallerCredentials: bool Versioned: bool @@ -93,12 +93,12 @@ class SyncConfigType(TypedDict, total=False): class CachingConfigType(TypedDict, total=False): - CachingKeys: List[str] + CachingKeys: list[str] Ttl: float class PipelineConfigType(TypedDict, total=False): - Functions: List[Intrinsicable[str]] + Functions: list[Intrinsicable[str]] class GraphQLApi(Resource): @@ -122,18 +122,18 @@ class GraphQLApi(Resource): Name: str AuthenticationType: str - LambdaAuthorizerConfig: Optional[LambdaAuthorizerConfigType] - OpenIDConnectConfig: Optional[OpenIDConnectConfigType] - UserPoolConfig: Optional[UserPoolConfigType] - AdditionalAuthenticationProviders: Optional[List[AdditionalAuthenticationProviderType]] - Tags: Optional[List[Dict[str, Any]]] - XrayEnabled: Optional[bool] - LogConfig: Optional[LogConfigType] - Visibility: Optional[str] - OwnerContact: Optional[str] - IntrospectionConfig: Optional[str] - QueryDepthLimit: Optional[int] - ResolverCountLimit: Optional[int] + LambdaAuthorizerConfig: LambdaAuthorizerConfigType | None + OpenIDConnectConfig: OpenIDConnectConfigType | None + UserPoolConfig: UserPoolConfigType | None + AdditionalAuthenticationProviders: list[AdditionalAuthenticationProviderType] | None + Tags: list[dict[str, Any]] | None + XrayEnabled: bool | None + LogConfig: LogConfigType | None + Visibility: str | None + OwnerContact: str | None + IntrospectionConfig: str | None + QueryDepthLimit: int | None + ResolverCountLimit: int | None runtime_attrs = {"api_id": lambda self: fnGetAtt(self.logical_id, "ApiId")} @@ -147,8 +147,8 @@ class GraphQLSchema(Resource): } ApiId: Intrinsicable[str] - Definition: Optional[str] - DefinitionS3Location: Optional[str] + Definition: str | None + DefinitionS3Location: str | None class DataSource(Resource): @@ -164,12 +164,12 @@ class DataSource(Resource): } ApiId: Intrinsicable[str] - Description: Optional[str] + Description: str | None Name: str Type: str ServiceRoleArn: str - DynamoDBConfig: Optional[DynamoDBConfigType] - LambdaConfig: Optional[LambdaConfigType] + DynamoDBConfig: DynamoDBConfigType | None + LambdaConfig: LambdaConfigType | None runtime_attrs = { "arn": lambda self: fnGetAtt(self.logical_id, "DataSourceArn"), @@ -194,12 +194,12 @@ class FunctionConfiguration(Resource): ApiId: Intrinsicable[str] DataSourceName: Intrinsicable[str] Name: str - Code: Optional[str] - CodeS3Location: Optional[str] - Description: Optional[str] - MaxBatchSize: Optional[int] - Runtime: Optional[AppSyncRuntimeType] - SyncConfig: Optional[SyncConfigType] + Code: str | None + CodeS3Location: str | None + Description: str | None + MaxBatchSize: int | None + Runtime: AppSyncRuntimeType | None + SyncConfig: SyncConfigType | None runtime_attrs = {"function_id": lambda self: fnGetAtt(self.logical_id, "FunctionId")} @@ -222,16 +222,16 @@ class Resolver(Resource): } ApiId: Intrinsicable[str] - CachingConfig: Optional[CachingConfigType] - Code: Optional[str] - CodeS3Location: Optional[str] - DataSourceName: Optional[str] + CachingConfig: CachingConfigType | None + Code: str | None + CodeS3Location: str | None + DataSourceName: str | None FieldName: str - Kind: Optional[str] - MaxBatchSize: Optional[int] - PipelineConfig: Optional[PipelineConfigType] - Runtime: Optional[AppSyncRuntimeType] - SyncConfig: Optional[SyncConfigType] + Kind: str | None + MaxBatchSize: int | None + PipelineConfig: PipelineConfigType | None + Runtime: AppSyncRuntimeType | None + SyncConfig: SyncConfigType | None TypeName: str @@ -245,9 +245,9 @@ class ApiKey(Resource): } ApiId: Intrinsicable[str] - ApiKeyId: Optional[str] - Description: Optional[str] - Expires: Optional[float] + ApiKeyId: str | None + Description: str | None + Expires: float | None class DomainName(Resource): @@ -260,7 +260,7 @@ class DomainName(Resource): CertificateArn: str DomainName: str - Description: Optional[str] + Description: str | None runtime_attrs = {"domain_name": lambda self: ref(self.logical_id)} @@ -291,5 +291,5 @@ class ApiCache(Resource): ApiId: Intrinsicable[str] Type: str Ttl: float - AtRestEncryptionEnabled: Optional[bool] - TransitEncryptionEnabled: Optional[bool] + AtRestEncryptionEnabled: bool | None + TransitEncryptionEnabled: bool | None diff --git a/samtranslator/internal/schema_source/aws_serverless_api.py b/samtranslator/internal/schema_source/aws_serverless_api.py index c478a3c3ae..62ad842525 100644 --- a/samtranslator/internal/schema_source/aws_serverless_api.py +++ b/samtranslator/internal/schema_source/aws_serverless_api.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Dict, List, Literal, Optional, Union +from typing import Literal, Union from samtranslator.internal.schema_source.aws_serverless_connector import EmbeddedConnector from samtranslator.internal.schema_source.common import ( @@ -38,137 +38,130 @@ class ResourcePolicy(BaseModel): - AwsAccountBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("AwsAccountBlacklist") - AwsAccountWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("AwsAccountWhitelist") - CustomStatements: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("CustomStatements") - IntrinsicVpcBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpcBlacklist") - IntrinsicVpcWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpcWhitelist") - IntrinsicVpceBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpceBlacklist") - IntrinsicVpceWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpceWhitelist") - IpRangeBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IpRangeBlacklist") - IpRangeWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IpRangeWhitelist") - SourceVpcBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("SourceVpcBlacklist") - SourceVpcWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("SourceVpcWhitelist") + AwsAccountBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("AwsAccountBlacklist") + AwsAccountWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("AwsAccountWhitelist") + CustomStatements: list[Union[str, DictStrAny]] | None = resourcepolicy("CustomStatements") + IntrinsicVpcBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpcBlacklist") + IntrinsicVpcWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpcWhitelist") + IntrinsicVpceBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpceBlacklist") + IntrinsicVpceWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpceWhitelist") + IpRangeBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("IpRangeBlacklist") + IpRangeWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("IpRangeWhitelist") + SourceVpcBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("SourceVpcBlacklist") + SourceVpcWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("SourceVpcWhitelist") class CognitoAuthorizerIdentity(BaseModel): - Header: Optional[str] = cognitoauthorizeridentity("Header") - ReauthorizeEvery: Optional[SamIntrinsicable[int]] = cognitoauthorizeridentity("ReauthorizeEvery") - ValidationExpression: Optional[str] = cognitoauthorizeridentity("ValidationExpression") + Header: str | None = cognitoauthorizeridentity("Header") + ReauthorizeEvery: SamIntrinsicable[int] | None = cognitoauthorizeridentity("ReauthorizeEvery") + ValidationExpression: str | None = cognitoauthorizeridentity("ValidationExpression") class CognitoAuthorizer(BaseModel): - AuthorizationScopes: Optional[List[str]] = cognitoauthorizer("AuthorizationScopes") - Identity: Optional[CognitoAuthorizerIdentity] = cognitoauthorizer("Identity") + AuthorizationScopes: list[str] | None = cognitoauthorizer("AuthorizationScopes") + Identity: CognitoAuthorizerIdentity | None = cognitoauthorizer("Identity") UserPoolArn: SamIntrinsicable[str] = cognitoauthorizer("UserPoolArn") class LambdaTokenAuthorizerIdentity(BaseModel): - ReauthorizeEvery: Optional[SamIntrinsicable[int]] = lambdatokenauthorizeridentity("ReauthorizeEvery") - ValidationExpression: Optional[str] = lambdatokenauthorizeridentity("ValidationExpression") - Header: Optional[str] = lambdatokenauthorizeridentity("Header") + ReauthorizeEvery: SamIntrinsicable[int] | None = lambdatokenauthorizeridentity("ReauthorizeEvery") + ValidationExpression: str | None = lambdatokenauthorizeridentity("ValidationExpression") + Header: str | None = lambdatokenauthorizeridentity("Header") class LambdaRequestAuthorizerIdentity(BaseModel): - Context: Optional[List[str]] = lambdarequestauthorizeridentity("Context") - Headers: Optional[List[str]] = lambdarequestauthorizeridentity("Headers") - QueryStrings: Optional[List[str]] = lambdarequestauthorizeridentity("QueryStrings") - ReauthorizeEvery: Optional[SamIntrinsicable[int]] = lambdarequestauthorizeridentity("ReauthorizeEvery") - StageVariables: Optional[List[str]] = lambdarequestauthorizeridentity("StageVariables") + Context: list[str] | None = lambdarequestauthorizeridentity("Context") + Headers: list[str] | None = lambdarequestauthorizeridentity("Headers") + QueryStrings: list[str] | None = lambdarequestauthorizeridentity("QueryStrings") + ReauthorizeEvery: SamIntrinsicable[int] | None = lambdarequestauthorizeridentity("ReauthorizeEvery") + StageVariables: list[str] | None = lambdarequestauthorizeridentity("StageVariables") class LambdaTokenAuthorizer(BaseModel): FunctionArn: SamIntrinsicable[str] = lambdatokenauthorizer("FunctionArn") - FunctionInvokeRole: Optional[str] = lambdatokenauthorizer("FunctionInvokeRole") - FunctionPayloadType: Optional[Literal["TOKEN"]] = lambdatokenauthorizer("FunctionPayloadType") - Identity: Optional[LambdaTokenAuthorizerIdentity] = lambdatokenauthorizer("Identity") - DisableFunctionDefaultPermissions: Optional[bool] = lambdatokenauthorizer("DisableFunctionDefaultPermissions") + FunctionInvokeRole: str | None = lambdatokenauthorizer("FunctionInvokeRole") + FunctionPayloadType: Literal["TOKEN"] | None = lambdatokenauthorizer("FunctionPayloadType") + Identity: LambdaTokenAuthorizerIdentity | None = lambdatokenauthorizer("Identity") + DisableFunctionDefaultPermissions: bool | None = lambdatokenauthorizer("DisableFunctionDefaultPermissions") class LambdaRequestAuthorizer(BaseModel): FunctionArn: SamIntrinsicable[str] = lambdarequestauthorizer("FunctionArn") - FunctionInvokeRole: Optional[str] = lambdarequestauthorizer("FunctionInvokeRole") - FunctionPayloadType: Optional[Literal["REQUEST"]] = lambdarequestauthorizer("FunctionPayloadType") - Identity: Optional[LambdaRequestAuthorizerIdentity] = lambdarequestauthorizer("Identity") - DisableFunctionDefaultPermissions: Optional[bool] = lambdarequestauthorizer("DisableFunctionDefaultPermissions") + FunctionInvokeRole: str | None = lambdarequestauthorizer("FunctionInvokeRole") + FunctionPayloadType: Literal["REQUEST"] | None = lambdarequestauthorizer("FunctionPayloadType") + Identity: LambdaRequestAuthorizerIdentity | None = lambdarequestauthorizer("Identity") + DisableFunctionDefaultPermissions: bool | None = lambdarequestauthorizer("DisableFunctionDefaultPermissions") class UsagePlan(BaseModel): CreateUsagePlan: SamIntrinsicable[Literal["PER_API", "SHARED", "NONE"]] = usageplan("CreateUsagePlan") - Description: Optional[PassThroughProp] = usageplan("Description") - Quota: Optional[PassThroughProp] = usageplan("Quota") - Tags: Optional[PassThroughProp] = usageplan("Tags") - Throttle: Optional[PassThroughProp] = usageplan("Throttle") - UsagePlanName: Optional[PassThroughProp] = usageplan("UsagePlanName") + Description: PassThroughProp | None = usageplan("Description") + Quota: PassThroughProp | None = usageplan("Quota") + Tags: PassThroughProp | None = usageplan("Tags") + Throttle: PassThroughProp | None = usageplan("Throttle") + UsagePlanName: PassThroughProp | None = usageplan("UsagePlanName") class Auth(BaseModel): - AddDefaultAuthorizerToCorsPreflight: Optional[bool] = auth("AddDefaultAuthorizerToCorsPreflight") - AddApiKeyRequiredToCorsPreflight: Optional[bool] = auth("AddApiKeyRequiredToCorsPreflight") - ApiKeyRequired: Optional[bool] = auth("ApiKeyRequired") - Authorizers: Optional[ - Dict[ - str, - Union[ - CognitoAuthorizer, - LambdaTokenAuthorizer, - LambdaRequestAuthorizer, - ], - ] - ] = auth("Authorizers") - DefaultAuthorizer: Optional[str] = auth("DefaultAuthorizer") - InvokeRole: Optional[str] = auth("InvokeRole") - ResourcePolicy: Optional[ResourcePolicy] = auth("ResourcePolicy") - UsagePlan: Optional[UsagePlan] = auth("UsagePlan") + AddDefaultAuthorizerToCorsPreflight: bool | None = auth("AddDefaultAuthorizerToCorsPreflight") + AddApiKeyRequiredToCorsPreflight: bool | None = auth("AddApiKeyRequiredToCorsPreflight") + ApiKeyRequired: bool | None = auth("ApiKeyRequired") + Authorizers: dict[str, Union[CognitoAuthorizer, LambdaTokenAuthorizer, LambdaRequestAuthorizer]] | None = auth( + "Authorizers" + ) + DefaultAuthorizer: str | None = auth("DefaultAuthorizer") + InvokeRole: str | None = auth("InvokeRole") + ResourcePolicy: ResourcePolicy | None = auth("ResourcePolicy") + UsagePlan: UsagePlan | None = auth("UsagePlan") class Cors(BaseModel): - AllowCredentials: Optional[bool] = cors("AllowCredentials") - AllowHeaders: Optional[str] = cors("AllowHeaders") - AllowMethods: Optional[str] = cors("AllowMethods") + AllowCredentials: bool | None = cors("AllowCredentials") + AllowHeaders: str | None = cors("AllowHeaders") + AllowMethods: str | None = cors("AllowMethods") AllowOrigin: str = cors("AllowOrigin") - MaxAge: Optional[str] = cors("MaxAge") + MaxAge: str | None = cors("MaxAge") class Route53(BaseModel): - DistributionDomainName: Optional[PassThroughProp] = passthrough_prop( + DistributionDomainName: PassThroughProp | None = passthrough_prop( ROUTE53_STEM, "DistributionDomainName", ["AWS::Route53::RecordSetGroup.AliasTarget", "DNSName"], ) - EvaluateTargetHealth: Optional[PassThroughProp] = passthrough_prop( + EvaluateTargetHealth: PassThroughProp | None = passthrough_prop( ROUTE53_STEM, "EvaluateTargetHealth", ["AWS::Route53::RecordSetGroup.AliasTarget", "EvaluateTargetHealth"], ) - HostedZoneId: Optional[PassThroughProp] = passthrough_prop( + HostedZoneId: PassThroughProp | None = passthrough_prop( ROUTE53_STEM, "HostedZoneId", ["AWS::Route53::RecordSetGroup.RecordSet", "HostedZoneId"], ) - HostedZoneName: Optional[PassThroughProp] = passthrough_prop( + HostedZoneName: PassThroughProp | None = passthrough_prop( ROUTE53_STEM, "HostedZoneName", ["AWS::Route53::RecordSetGroup.RecordSet", "HostedZoneName"], ) - IpV6: Optional[bool] = route53("IpV6") - SetIdentifier: Optional[PassThroughProp] = passthrough_prop( + IpV6: bool | None = route53("IpV6") + SetIdentifier: PassThroughProp | None = passthrough_prop( ROUTE53_STEM, "SetIdentifier", ["AWS::Route53::RecordSetGroup.RecordSet", "SetIdentifier"], ) - Region: Optional[PassThroughProp] = passthrough_prop( + Region: PassThroughProp | None = passthrough_prop( ROUTE53_STEM, "Region", ["AWS::Route53::RecordSetGroup.RecordSet", "Region"], ) - SeparateRecordSetGroup: Optional[bool] # SAM-specific property - not yet documented in sam-docs.json - VpcEndpointDomainName: Optional[PassThroughProp] = passthrough_prop( + SeparateRecordSetGroup: bool | None # SAM-specific property - not yet documented in sam-docs.json + VpcEndpointDomainName: PassThroughProp | None = passthrough_prop( ROUTE53_STEM, "VpcEndpointDomainName", ["AWS::Route53::RecordSet.AliasTarget", "DNSName"], ) - VpcEndpointHostedZoneId: Optional[PassThroughProp] = passthrough_prop( + VpcEndpointHostedZoneId: PassThroughProp | None = passthrough_prop( ROUTE53_STEM, "VpcEndpointHostedZoneId", ["AWS::Route53::RecordSet.AliasTarget", "HostedZoneId"], @@ -184,36 +177,36 @@ class AccessAssociation(BaseModel): class Domain(BaseModel): - BasePath: Optional[PassThroughProp] = domain("BasePath") - NormalizeBasePath: Optional[bool] = domain("NormalizeBasePath") - Policy: Optional[PassThroughProp] + BasePath: PassThroughProp | None = domain("BasePath") + NormalizeBasePath: bool | None = domain("NormalizeBasePath") + Policy: PassThroughProp | None CertificateArn: PassThroughProp = domain("CertificateArn") DomainName: PassThroughProp = passthrough_prop( DOMAIN_STEM, "DomainName", ["AWS::ApiGateway::DomainName", "Properties", "DomainName"], ) - EndpointConfiguration: Optional[SamIntrinsicable[Literal["REGIONAL", "EDGE", "PRIVATE"]]] = domain( + EndpointConfiguration: SamIntrinsicable[Literal["REGIONAL", "EDGE", "PRIVATE"]] | None = domain( "EndpointConfiguration" ) - IpAddressType: Optional[PassThroughProp] # TODO: add documentation; currently unavailable - MutualTlsAuthentication: Optional[PassThroughProp] = passthrough_prop( + IpAddressType: PassThroughProp | None # TODO: add documentation; currently unavailable + MutualTlsAuthentication: PassThroughProp | None = passthrough_prop( DOMAIN_STEM, "MutualTlsAuthentication", ["AWS::ApiGateway::DomainName", "Properties", "MutualTlsAuthentication"], ) - OwnershipVerificationCertificateArn: Optional[PassThroughProp] = passthrough_prop( + OwnershipVerificationCertificateArn: PassThroughProp | None = passthrough_prop( DOMAIN_STEM, "OwnershipVerificationCertificateArn", ["AWS::ApiGateway::DomainName", "Properties", "OwnershipVerificationCertificateArn"], ) - Route53: Optional[Route53] = domain("Route53") - SecurityPolicy: Optional[PassThroughProp] = passthrough_prop( + Route53: Route53 | None = domain("Route53") + SecurityPolicy: PassThroughProp | None = passthrough_prop( DOMAIN_STEM, "SecurityPolicy", ["AWS::ApiGateway::DomainName", "Properties", "SecurityPolicy"], ) - AccessAssociation: Optional[AccessAssociation] + AccessAssociation: AccessAssociation | None class DefinitionUri(BaseModel): @@ -227,7 +220,7 @@ class DefinitionUri(BaseModel): "Key", ["AWS::ApiGateway::RestApi.S3Location", "Key"], ) - Version: Optional[PassThroughProp] = passthrough_prop( + Version: PassThroughProp | None = passthrough_prop( DEFINITION_URI_STEM, "Version", ["AWS::ApiGateway::RestApi.S3Location", "Version"], @@ -235,186 +228,186 @@ class DefinitionUri(BaseModel): class EndpointConfiguration(BaseModel): - Type: Optional[PassThroughProp] = passthrough_prop( + Type: PassThroughProp | None = passthrough_prop( ENDPOINT_CONFIGURATION_STEM, "Type", ["AWS::ApiGateway::RestApi.EndpointConfiguration", "Types"], ) - VPCEndpointIds: Optional[PassThroughProp] = passthrough_prop( + VPCEndpointIds: PassThroughProp | None = passthrough_prop( ENDPOINT_CONFIGURATION_STEM, "VPCEndpointIds", ["AWS::ApiGateway::RestApi.EndpointConfiguration", "VpcEndpointIds"], ) - IpAddressType: Optional[PassThroughProp] # TODO: add documentation; currently unavailable + IpAddressType: PassThroughProp | None # TODO: add documentation; currently unavailable -Name = Optional[PassThroughProp] -DefinitionUriType = Optional[Union[str, DefinitionUri]] -MergeDefinitions = Optional[bool] -CacheClusterEnabled = Optional[PassThroughProp] -CacheClusterSize = Optional[PassThroughProp] -Variables = Optional[PassThroughProp] -EndpointConfigurationType = Optional[SamIntrinsicable[EndpointConfiguration]] -MethodSettings = Optional[PassThroughProp] -BinaryMediaTypes = Optional[PassThroughProp] -MinimumCompressionSize = Optional[PassThroughProp] -CorsType = Optional[SamIntrinsicable[Union[str, Cors]]] -GatewayResponses = Optional[DictStrAny] -AccessLogSetting = Optional[PassThroughProp] -CanarySetting = Optional[PassThroughProp] -TracingEnabled = Optional[PassThroughProp] -OpenApiVersion = Optional[Union[float, str]] # TODO: float doesn't exist in documentation -AlwaysDeploy = Optional[bool] +Name = PassThroughProp | None +DefinitionUriType = Union[str, DefinitionUri] | None +MergeDefinitions = bool | None +CacheClusterEnabled = PassThroughProp | None +CacheClusterSize = PassThroughProp | None +Variables = PassThroughProp | None +EndpointConfigurationType = SamIntrinsicable[EndpointConfiguration] | None +MethodSettings = PassThroughProp | None +BinaryMediaTypes = PassThroughProp | None +MinimumCompressionSize = PassThroughProp | None +CorsType = SamIntrinsicable[Union[str, Cors]] | None +GatewayResponses = DictStrAny | None +AccessLogSetting = PassThroughProp | None +CanarySetting = PassThroughProp | None +TracingEnabled = PassThroughProp | None +OpenApiVersion = Union[float, str] | None # TODO: float doesn't exist in documentation +AlwaysDeploy = bool | None class Properties(BaseModel): - AccessLogSetting: Optional[AccessLogSetting] = passthrough_prop( + AccessLogSetting: AccessLogSetting | None = passthrough_prop( PROPERTIES_STEM, "AccessLogSetting", ["AWS::ApiGateway::Stage", "Properties", "AccessLogSetting"], ) - ApiKeySourceType: Optional[PassThroughProp] = passthrough_prop( + ApiKeySourceType: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "ApiKeySourceType", ["AWS::ApiGateway::RestApi", "Properties", "ApiKeySourceType"], ) - Auth: Optional[Auth] = properties("Auth") - BinaryMediaTypes: Optional[BinaryMediaTypes] = properties("BinaryMediaTypes") - CacheClusterEnabled: Optional[CacheClusterEnabled] = passthrough_prop( + Auth: Auth | None = properties("Auth") + BinaryMediaTypes: BinaryMediaTypes | None = properties("BinaryMediaTypes") + CacheClusterEnabled: CacheClusterEnabled | None = passthrough_prop( PROPERTIES_STEM, "CacheClusterEnabled", ["AWS::ApiGateway::Stage", "Properties", "CacheClusterEnabled"], ) - CacheClusterSize: Optional[CacheClusterSize] = passthrough_prop( + CacheClusterSize: CacheClusterSize | None = passthrough_prop( PROPERTIES_STEM, "CacheClusterSize", ["AWS::ApiGateway::Stage", "Properties", "CacheClusterSize"], ) - CanarySetting: Optional[CanarySetting] = passthrough_prop( + CanarySetting: CanarySetting | None = passthrough_prop( PROPERTIES_STEM, "CanarySetting", ["AWS::ApiGateway::Stage", "Properties", "CanarySetting"], ) - Cors: Optional[CorsType] = properties("Cors") - DefinitionBody: Optional[DictStrAny] = properties("DefinitionBody") - DefinitionUri: Optional[DefinitionUriType] = properties("DefinitionUri") - MergeDefinitions: Optional[MergeDefinitions] = properties("MergeDefinitions") - Description: Optional[PassThroughProp] = passthrough_prop( + Cors: CorsType | None = properties("Cors") + DefinitionBody: DictStrAny | None = properties("DefinitionBody") + DefinitionUri: DefinitionUriType | None = properties("DefinitionUri") + MergeDefinitions: MergeDefinitions | None = properties("MergeDefinitions") + Description: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "Description", ["AWS::ApiGateway::Stage", "Properties", "Description"], ) - DisableExecuteApiEndpoint: Optional[PassThroughProp] = properties("DisableExecuteApiEndpoint") - Domain: Optional[Domain] = properties("Domain") - EndpointConfiguration: Optional[EndpointConfigurationType] = properties("EndpointConfiguration") - FailOnWarnings: Optional[PassThroughProp] = passthrough_prop( + DisableExecuteApiEndpoint: PassThroughProp | None = properties("DisableExecuteApiEndpoint") + Domain: Domain | None = properties("Domain") + EndpointConfiguration: EndpointConfigurationType | None = properties("EndpointConfiguration") + FailOnWarnings: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "FailOnWarnings", ["AWS::ApiGateway::RestApi", "Properties", "FailOnWarnings"], ) - GatewayResponses: Optional[GatewayResponses] = properties("GatewayResponses") - MethodSettings: Optional[MethodSettings] = passthrough_prop( + GatewayResponses: GatewayResponses | None = properties("GatewayResponses") + MethodSettings: MethodSettings | None = passthrough_prop( PROPERTIES_STEM, "MethodSettings", ["AWS::ApiGateway::Stage", "Properties", "MethodSettings"], ) - MinimumCompressionSize: Optional[MinimumCompressionSize] = passthrough_prop( + MinimumCompressionSize: MinimumCompressionSize | None = passthrough_prop( PROPERTIES_STEM, "MinimumCompressionSize", ["AWS::ApiGateway::RestApi", "Properties", "MinimumCompressionSize"], ) - Mode: Optional[PassThroughProp] = passthrough_prop( + Mode: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "Mode", ["AWS::ApiGateway::RestApi", "Properties", "Mode"], ) - Models: Optional[DictStrAny] = properties("Models") - Name: Optional[Name] = passthrough_prop( + Models: DictStrAny | None = properties("Models") + Name: Name | None = passthrough_prop( PROPERTIES_STEM, "Name", ["AWS::ApiGateway::RestApi", "Properties", "Name"], ) - OpenApiVersion: Optional[OpenApiVersion] = properties("OpenApiVersion") + OpenApiVersion: OpenApiVersion | None = properties("OpenApiVersion") StageName: SamIntrinsicable[str] = properties("StageName") - Tags: Optional[DictStrAny] = properties("Tags") - Policy: Optional[PassThroughProp] = passthrough_prop( + Tags: DictStrAny | None = properties("Tags") + Policy: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "Policy", ["AWS::ApiGateway::RestApi", "Properties", "Policy"], ) - PropagateTags: Optional[bool] = properties("PropagateTags") - SecurityPolicy: Optional[PassThroughProp] = passthrough_prop( + PropagateTags: bool | None = properties("PropagateTags") + SecurityPolicy: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "SecurityPolicy", ["AWS::ApiGateway::RestApi", "Properties", "SecurityPolicy"], ) - TracingEnabled: Optional[TracingEnabled] = passthrough_prop( + TracingEnabled: TracingEnabled | None = passthrough_prop( PROPERTIES_STEM, "TracingEnabled", ["AWS::ApiGateway::Stage", "Properties", "TracingEnabled"], ) - Variables: Optional[Variables] = passthrough_prop( + Variables: Variables | None = passthrough_prop( PROPERTIES_STEM, "Variables", ["AWS::ApiGateway::Stage", "Properties", "Variables"], ) - AlwaysDeploy: Optional[AlwaysDeploy] = properties("AlwaysDeploy") + AlwaysDeploy: AlwaysDeploy | None = properties("AlwaysDeploy") class Globals(BaseModel): - Auth: Optional[Auth] = properties("Auth") - Name: Optional[Name] = passthrough_prop( + Auth: Auth | None = properties("Auth") + Name: Name | None = passthrough_prop( PROPERTIES_STEM, "Name", ["AWS::ApiGateway::RestApi", "Properties", "Name"], ) - DefinitionUri: Optional[PassThroughProp] = properties("DefinitionUri") - CacheClusterEnabled: Optional[CacheClusterEnabled] = passthrough_prop( + DefinitionUri: PassThroughProp | None = properties("DefinitionUri") + CacheClusterEnabled: CacheClusterEnabled | None = passthrough_prop( PROPERTIES_STEM, "CacheClusterEnabled", ["AWS::ApiGateway::Stage", "Properties", "CacheClusterEnabled"], ) - CacheClusterSize: Optional[CacheClusterSize] = passthrough_prop( + CacheClusterSize: CacheClusterSize | None = passthrough_prop( PROPERTIES_STEM, "CacheClusterSize", ["AWS::ApiGateway::Stage", "Properties", "CacheClusterSize"], ) - MergeDefinitions: Optional[MergeDefinitions] = properties("MergeDefinitions") - Variables: Optional[Variables] = passthrough_prop( + MergeDefinitions: MergeDefinitions | None = properties("MergeDefinitions") + Variables: Variables | None = passthrough_prop( PROPERTIES_STEM, "Variables", ["AWS::ApiGateway::Stage", "Properties", "Variables"], ) - EndpointConfiguration: Optional[PassThroughProp] = properties("EndpointConfiguration") - MethodSettings: Optional[MethodSettings] = properties("MethodSettings") - BinaryMediaTypes: Optional[BinaryMediaTypes] = properties("BinaryMediaTypes") - MinimumCompressionSize: Optional[MinimumCompressionSize] = passthrough_prop( + EndpointConfiguration: PassThroughProp | None = properties("EndpointConfiguration") + MethodSettings: MethodSettings | None = properties("MethodSettings") + BinaryMediaTypes: BinaryMediaTypes | None = properties("BinaryMediaTypes") + MinimumCompressionSize: MinimumCompressionSize | None = passthrough_prop( PROPERTIES_STEM, "MinimumCompressionSize", ["AWS::ApiGateway::RestApi", "Properties", "MinimumCompressionSize"], ) - Cors: Optional[CorsType] = properties("Cors") - GatewayResponses: Optional[GatewayResponses] = properties("GatewayResponses") - AccessLogSetting: Optional[AccessLogSetting] = passthrough_prop( + Cors: CorsType | None = properties("Cors") + GatewayResponses: GatewayResponses | None = properties("GatewayResponses") + AccessLogSetting: AccessLogSetting | None = passthrough_prop( PROPERTIES_STEM, "AccessLogSetting", ["AWS::ApiGateway::Stage", "Properties", "AccessLogSetting"], ) - CanarySetting: Optional[CanarySetting] = passthrough_prop( + CanarySetting: CanarySetting | None = passthrough_prop( PROPERTIES_STEM, "CanarySetting", ["AWS::ApiGateway::Stage", "Properties", "CanarySetting"], ) - TracingEnabled: Optional[TracingEnabled] = passthrough_prop( + TracingEnabled: TracingEnabled | None = passthrough_prop( PROPERTIES_STEM, "TracingEnabled", ["AWS::ApiGateway::Stage", "Properties", "TracingEnabled"], ) - OpenApiVersion: Optional[OpenApiVersion] = properties("OpenApiVersion") - Domain: Optional[Domain] = properties("Domain") - AlwaysDeploy: Optional[AlwaysDeploy] = properties("AlwaysDeploy") - PropagateTags: Optional[bool] = properties("PropagateTags") - SecurityPolicy: Optional[PassThroughProp] = passthrough_prop( + OpenApiVersion: OpenApiVersion | None = properties("OpenApiVersion") + Domain: Domain | None = properties("Domain") + AlwaysDeploy: AlwaysDeploy | None = properties("AlwaysDeploy") + PropagateTags: bool | None = properties("PropagateTags") + SecurityPolicy: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "SecurityPolicy", ["AWS::ApiGateway::RestApi", "Properties", "SecurityPolicy"], @@ -424,4 +417,4 @@ class Globals(BaseModel): class Resource(ResourceAttributes): Type: Literal["AWS::Serverless::Api"] Properties: Properties - Connectors: Optional[Dict[str, EmbeddedConnector]] + Connectors: dict[str, EmbeddedConnector] | None diff --git a/samtranslator/internal/schema_source/aws_serverless_application.py b/samtranslator/internal/schema_source/aws_serverless_application.py index 7079ca17ca..8b93175225 100644 --- a/samtranslator/internal/schema_source/aws_serverless_application.py +++ b/samtranslator/internal/schema_source/aws_serverless_application.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, Literal, Optional, Union +from typing import Any, Literal, Union from samtranslator.internal.schema_source.common import ( BaseModel, @@ -24,18 +24,18 @@ class Location(BaseModel): class Properties(BaseModel): Location: Union[str, Location] = properties("Location") - NotificationARNs: Optional[PassThroughProp] = passthrough_prop( + NotificationARNs: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "NotificationARNs", ["AWS::CloudFormation::Stack", "Properties", "NotificationARNs"], ) - Parameters: Optional[PassThroughProp] = passthrough_prop( + Parameters: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "Parameters", ["AWS::CloudFormation::Stack", "Properties", "Parameters"], ) - Tags: Optional[Dict[str, Any]] = properties("Tags") - TimeoutInMinutes: Optional[PassThroughProp] = passthrough_prop( + Tags: dict[str, Any] | None = properties("Tags") + TimeoutInMinutes: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "TimeoutInMinutes", ["AWS::CloudFormation::Stack", "Properties", "TimeoutInMinutes"], diff --git a/samtranslator/internal/schema_source/aws_serverless_capacity_provider.py b/samtranslator/internal/schema_source/aws_serverless_capacity_provider.py index 9d9443eb05..28b504108c 100644 --- a/samtranslator/internal/schema_source/aws_serverless_capacity_provider.py +++ b/samtranslator/internal/schema_source/aws_serverless_capacity_provider.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import List, Literal, Optional +from typing import Literal from samtranslator.internal.schema_source.common import ( BaseModel, @@ -25,35 +25,35 @@ class VpcConfig(BaseModel): # Optional list of security group IDs - supports intrinsic functions for dynamic references - SecurityGroupIds: Optional[SamIntrinsicable[List[SamIntrinsicable[str]]]] = vpcconfig("SecurityGroupIds") + SecurityGroupIds: SamIntrinsicable[list[SamIntrinsicable[str]]] | None = vpcconfig("SecurityGroupIds") # Required list of subnet IDs - supports intrinsic functions for dynamic VPC configuration - SubnetIds: SamIntrinsicable[List[SamIntrinsicable[str]]] = vpcconfig("SubnetIds") + SubnetIds: SamIntrinsicable[list[SamIntrinsicable[str]]] = vpcconfig("SubnetIds") class InstanceRequirements(BaseModel): # Optional list of CPU architectures - maps to CFN InstanceRequirements.Architecture - # Uses SamIntrinsicable[List[SamIntrinsicable[str]]] to support intrinsic functions like !Ref for both list and list item - Architectures: Optional[SamIntrinsicable[List[SamIntrinsicable[str]]]] = instancerequirements("Architectures") + # Uses SamIntrinsicable[list[SamIntrinsicable[str]]] to support intrinsic functions like !Ref for both list and list item + Architectures: SamIntrinsicable[list[SamIntrinsicable[str]]] | None = instancerequirements("Architectures") # Optional list of allowed EC2 instance types - maps to CFN InstanceRequirements.AllowedInstanceTypes - # Uses SamIntrinsicable[List[SamIntrinsicable[str]]] to support intrinsic functions like !Ref for both list and list item - AllowedTypes: Optional[SamIntrinsicable[List[SamIntrinsicable[str]]]] = instancerequirements("AllowedTypes") + # Uses SamIntrinsicable[list[SamIntrinsicable[str]]] to support intrinsic functions like !Ref for both list and list item + AllowedTypes: SamIntrinsicable[list[SamIntrinsicable[str]]] | None = instancerequirements("AllowedTypes") # Optional list of excluded EC2 instance types - maps to CFN InstanceRequirements.ExcludedInstanceTypes - # Uses SamIntrinsicable[List[SamIntrinsicable[str]]] to support intrinsic functions like !Ref for both list and list item - ExcludedTypes: Optional[SamIntrinsicable[List[SamIntrinsicable[str]]]] = instancerequirements("ExcludedTypes") + # Uses SamIntrinsicable[list[SamIntrinsicable[str]]] to support intrinsic functions like !Ref for both list and list item + ExcludedTypes: SamIntrinsicable[list[SamIntrinsicable[str]]] | None = instancerequirements("ExcludedTypes") class ScalingConfig(BaseModel): # Optional maximum instance count - maps to CFN CapacityProviderScalingConfig.MaxVCpuCount # Uses SamIntrinsicable[int] to support dynamic scaling limits via parameters/conditions - MaxVCpuCount: Optional[SamIntrinsicable[int]] = scalingconfig("MaxVCpuCount") + MaxVCpuCount: SamIntrinsicable[int] | None = scalingconfig("MaxVCpuCount") # Average CPU utilization target (0-100) - maps to CFN ScalingPolicies with CPU metric type # When specified, automatically sets ScalingMode to "Manual" # Uses SamIntrinsicable[float] to support dynamic scaling targets via parameters/conditions - AverageCPUUtilization: Optional[SamIntrinsicable[float]] = scalingconfig("AverageCPUUtilization") + AverageCPUUtilization: SamIntrinsicable[float] | None = scalingconfig("AverageCPUUtilization") class Properties(BaseModel): - CapacityProviderName: Optional[PassThroughProp] = passthrough_prop( + CapacityProviderName: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "CapacityProviderName", ["AWS::Lambda::CapacityProvider", "Properties", "CapacityProviderName"], @@ -64,25 +64,25 @@ class Properties(BaseModel): VpcConfig: VpcConfig = properties("VpcConfig") # Optional operator role ARN - if not provided, SAM auto-generates one with EC2 management permissions - OperatorRole: Optional[PassThroughProp] = properties("OperatorRole") + OperatorRole: PassThroughProp | None = properties("OperatorRole") # Optional tags - SAM transforms key-value pairs to CFN Tag objects before passing to CFN # Uses DictStrAny to support flexible tag structure with string keys and any values - Tags: Optional[DictStrAny] = properties("Tags") + Tags: DictStrAny | None = properties("Tags") # Optional flag to propagate tags to resources created by this capacity provider # When true, all tags defined on the capacity provider will be propagated to generated resources - PropagateTags: Optional[bool] = properties("PropagateTags") + PropagateTags: bool | None = properties("PropagateTags") # Optional instance requirements - maps to CFN InstanceRequirements with property name shortening # Uses custom InstanceRequirements class because SAM shortens names - InstanceRequirements: Optional[InstanceRequirements] = properties("InstanceRequirements") + InstanceRequirements: InstanceRequirements | None = properties("InstanceRequirements") # Optional scaling configuration - maps to CFN CapacityProviderScalingConfig # Uses custom ScalingConfig class because SAM renames construct (CapacityProviderScalingConfig→ScalingConfig) - ScalingConfig: Optional[ScalingConfig] = properties("ScalingConfig") + ScalingConfig: ScalingConfig | None = properties("ScalingConfig") - KmsKeyArn: Optional[PassThroughProp] = passthrough_prop( + KmsKeyArn: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "KmsKeyArn", ["AWS::Lambda::CapacityProvider", "Properties", "KmsKeyArn"], @@ -92,28 +92,28 @@ class Properties(BaseModel): class Globals(BaseModel): # Global VPC configuration - can be inherited by capacity providers if not overridden # Uses custom VpcConfig class to validate required SubnetIds while maintaining passthrough behavior - VpcConfig: Optional[VpcConfig] = properties("VpcConfig") + VpcConfig: VpcConfig | None = properties("VpcConfig") # Global operator role ARN - can be inherited by capacity providers if not overridden - OperatorRole: Optional[PassThroughProp] = properties("OperatorRole") + OperatorRole: PassThroughProp | None = properties("OperatorRole") # Global tags - can be inherited and merged with resource-specific tags # Uses DictStrAny to support flexible tag structure with string keys and any values - Tags: Optional[DictStrAny] = properties("Tags") + Tags: DictStrAny | None = properties("Tags") # Global flag to propagate tags to resources created by capacity providers # When true, all tags defined on capacity providers will be propagated to generated resources - PropagateTags: Optional[bool] = properties("PropagateTags") + PropagateTags: bool | None = properties("PropagateTags") # Global instance requirements - can be inherited by capacity providers if not overridden # Uses custom InstanceRequirements class because SAM shortens names - InstanceRequirements: Optional[InstanceRequirements] = properties("InstanceRequirements") + InstanceRequirements: InstanceRequirements | None = properties("InstanceRequirements") # Global scaling configuration - can be inherited by capacity providers if not overridden # Uses custom ScalingConfig class because SAM renames construct (CapacityProviderScalingConfig→ScalingConfig) - ScalingConfig: Optional[ScalingConfig] = properties("ScalingConfig") + ScalingConfig: ScalingConfig | None = properties("ScalingConfig") - KmsKeyArn: Optional[PassThroughProp] = passthrough_prop( + KmsKeyArn: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "KmsKeyArn", ["AWS::Lambda::CapacityProvider", "Properties", "KmsKeyArn"], diff --git a/samtranslator/internal/schema_source/aws_serverless_connector.py b/samtranslator/internal/schema_source/aws_serverless_connector.py index 76b596cb9e..f9319d2b87 100644 --- a/samtranslator/internal/schema_source/aws_serverless_connector.py +++ b/samtranslator/internal/schema_source/aws_serverless_connector.py @@ -1,4 +1,4 @@ -from typing import List, Literal, Optional, Union +from typing import Literal, Union from samtranslator.internal.schema_source.common import ( BaseModel, @@ -14,20 +14,20 @@ class ResourceReference(BaseModel): - Id: Optional[str] = resourcereference("Id") - Arn: Optional[PassThroughProp] = resourcereference("Arn") - Name: Optional[PassThroughProp] = resourcereference("Name") - Qualifier: Optional[PassThroughProp] = resourcereference("Qualifier") - QueueUrl: Optional[PassThroughProp] = resourcereference("QueueUrl") - ResourceId: Optional[PassThroughProp] = resourcereference("ResourceId") - RoleName: Optional[PassThroughProp] = resourcereference("RoleName") - Type: Optional[str] = resourcereference("Type") + Id: str | None = resourcereference("Id") + Arn: PassThroughProp | None = resourcereference("Arn") + Name: PassThroughProp | None = resourcereference("Name") + Qualifier: PassThroughProp | None = resourcereference("Qualifier") + QueueUrl: PassThroughProp | None = resourcereference("QueueUrl") + ResourceId: PassThroughProp | None = resourcereference("ResourceId") + RoleName: PassThroughProp | None = resourcereference("RoleName") + Type: str | None = resourcereference("Type") class Properties(BaseModel): Source: ResourceReference = properties("Source") - Destination: Union[ResourceReference, List[ResourceReference]] = properties("Destination") - Permissions: List[Literal["Read", "Write"]] = properties("Permissions") + Destination: Union[ResourceReference, list[ResourceReference]] = properties("Destination") + Permissions: list[Literal["Read", "Write"]] = properties("Permissions") class Resource(ResourceAttributes): @@ -36,12 +36,12 @@ class Resource(ResourceAttributes): class SourceReferenceProperties(BaseModel): - Qualifier: Optional[PassThroughProp] = sourcereference("Qualifier") + Qualifier: PassThroughProp | None = sourcereference("Qualifier") class EmbeddedConnectorProperties(BaseModel): - SourceReference: Optional[SourceReferenceProperties] = properties("SourceReference") - Destination: Union[ResourceReference, List[ResourceReference]] = properties("Destination") + SourceReference: SourceReferenceProperties | None = properties("SourceReference") + Destination: Union[ResourceReference, list[ResourceReference]] = properties("Destination") Permissions: PermissionsType = properties("Permissions") diff --git a/samtranslator/internal/schema_source/aws_serverless_function.py b/samtranslator/internal/schema_source/aws_serverless_function.py index cc35742864..811f37d1bf 100644 --- a/samtranslator/internal/schema_source/aws_serverless_function.py +++ b/samtranslator/internal/schema_source/aws_serverless_function.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Dict, List, Literal, Optional, Union +from typing import Literal, Union from samtranslator.internal.schema_source.aws_serverless_connector import EmbeddedConnector from samtranslator.internal.schema_source.common import ( @@ -59,42 +59,42 @@ class ResourcePolicy(BaseModel): - AwsAccountBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("AwsAccountBlacklist") - AwsAccountWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("AwsAccountWhitelist") - CustomStatements: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("CustomStatements") - IntrinsicVpcBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpcBlacklist") - IntrinsicVpcWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpcWhitelist") - IntrinsicVpceBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpceBlacklist") - IntrinsicVpceWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpceWhitelist") - IpRangeBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IpRangeBlacklist") - IpRangeWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IpRangeWhitelist") - SourceVpcBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("SourceVpcBlacklist") - SourceVpcWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("SourceVpcWhitelist") + AwsAccountBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("AwsAccountBlacklist") + AwsAccountWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("AwsAccountWhitelist") + CustomStatements: list[Union[str, DictStrAny]] | None = resourcepolicy("CustomStatements") + IntrinsicVpcBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpcBlacklist") + IntrinsicVpcWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpcWhitelist") + IntrinsicVpceBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpceBlacklist") + IntrinsicVpceWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpceWhitelist") + IpRangeBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("IpRangeBlacklist") + IpRangeWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("IpRangeWhitelist") + SourceVpcBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("SourceVpcBlacklist") + SourceVpcWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("SourceVpcWhitelist") class CodeUri(BaseModel): Bucket: SamIntrinsicable[str] = codeuri("Bucket") Key: SamIntrinsicable[str] = codeuri("Key") - Version: Optional[SamIntrinsicable[str]] = codeuri("Version") + Version: SamIntrinsicable[str] | None = codeuri("Version") class Hooks(BaseModel): - PostTraffic: Optional[SamIntrinsicable[str]] = hooks("PostTraffic") - PreTraffic: Optional[SamIntrinsicable[str]] = hooks("PreTraffic") + PostTraffic: SamIntrinsicable[str] | None = hooks("PostTraffic") + PreTraffic: SamIntrinsicable[str] | None = hooks("PreTraffic") class DeploymentPreference(BaseModel): - Alarms: Optional[SamIntrinsicable[List[DictStrAny]]] = deploymentpreference("Alarms") - Enabled: Optional[SamIntrinsicable[bool]] = deploymentpreference("Enabled") - Hooks: Optional[Hooks] = deploymentpreference("Hooks") - PassthroughCondition: Optional[SamIntrinsicable[bool]] = deploymentpreference("PassthroughCondition") - Role: Optional[SamIntrinsicable[str]] = deploymentpreference("Role") - TriggerConfigurations: Optional[PassThroughProp] = passthrough_prop( + Alarms: SamIntrinsicable[list[DictStrAny]] | None = deploymentpreference("Alarms") + Enabled: SamIntrinsicable[bool] | None = deploymentpreference("Enabled") + Hooks: Hooks | None = deploymentpreference("Hooks") + PassthroughCondition: SamIntrinsicable[bool] | None = deploymentpreference("PassthroughCondition") + Role: SamIntrinsicable[str] | None = deploymentpreference("Role") + TriggerConfigurations: PassThroughProp | None = passthrough_prop( DEPLOYMENT_PREFERENCE_STEM, "TriggerConfigurations", ["AWS::CodeDeploy::DeploymentGroup", "Properties", "TriggerConfigurations"], ) - Type: Optional[SamIntrinsicable[str]] = deploymentpreference( + Type: SamIntrinsicable[str] | None = deploymentpreference( "Type" ) # TODO: Should investigate whether this is a required field. This is a required field on documentation. However, we don't seem to use this field. @@ -105,30 +105,30 @@ class DeadLetterQueue(BaseModel): class EventInvokeOnFailure(BaseModel): - Destination: Optional[SamIntrinsicable[str]] = eventinvokeonfailure("Destination") - Type: Optional[Literal["SQS", "SNS", "Lambda", "EventBridge", "S3Bucket"]] = eventinvokeonfailure("Type") + Destination: SamIntrinsicable[str] | None = eventinvokeonfailure("Destination") + Type: Literal["SQS", "SNS", "Lambda", "EventBridge", "S3Bucket"] | None = eventinvokeonfailure("Type") class EventInvokeOnSuccess(BaseModel): - Destination: Optional[SamIntrinsicable[str]] = eventinvokeonsuccess("Destination") - Type: Optional[Literal["SQS", "SNS", "Lambda", "EventBridge", "S3Bucket"]] = eventinvokeonsuccess("Type") + Destination: SamIntrinsicable[str] | None = eventinvokeonsuccess("Destination") + Type: Literal["SQS", "SNS", "Lambda", "EventBridge", "S3Bucket"] | None = eventinvokeonsuccess("Type") class EventInvokeDestinationConfig(BaseModel): - OnFailure: Optional[EventInvokeOnFailure] = eventinvokedestinationconfig("OnFailure") - OnSuccess: Optional[EventInvokeOnSuccess] = eventinvokedestinationconfig("OnSuccess") + OnFailure: EventInvokeOnFailure | None = eventinvokedestinationconfig("OnFailure") + OnSuccess: EventInvokeOnSuccess | None = eventinvokedestinationconfig("OnSuccess") class EventInvokeConfig(BaseModel): - DestinationConfig: Optional[EventInvokeDestinationConfig] = eventinvokeconfig("DestinationConfig") - MaximumEventAgeInSeconds: Optional[int] = eventinvokeconfig("MaximumEventAgeInSeconds") - MaximumRetryAttempts: Optional[int] = eventinvokeconfig("MaximumRetryAttempts") + DestinationConfig: EventInvokeDestinationConfig | None = eventinvokeconfig("DestinationConfig") + MaximumEventAgeInSeconds: int | None = eventinvokeconfig("MaximumEventAgeInSeconds") + MaximumRetryAttempts: int | None = eventinvokeconfig("MaximumRetryAttempts") class S3EventProperties(BaseModel): Bucket: SamIntrinsicable[str] = s3eventproperties("Bucket") Events: PassThroughProp = s3eventproperties("Events") - Filter: Optional[PassThroughProp] = s3eventproperties("Filter") + Filter: PassThroughProp | None = s3eventproperties("Filter") class S3Event(BaseModel): @@ -137,22 +137,22 @@ class S3Event(BaseModel): class SqsSubscription(BaseModel): - BatchSize: Optional[SamIntrinsicable[str]] = sqssubscription("BatchSize") - Enabled: Optional[bool] = sqssubscription("Enabled") + BatchSize: SamIntrinsicable[str] | None = sqssubscription("BatchSize") + Enabled: bool | None = sqssubscription("Enabled") QueueArn: SamIntrinsicable[str] = sqssubscription("QueueArn") - QueuePolicyLogicalId: Optional[str] = sqssubscription("QueuePolicyLogicalId") + QueuePolicyLogicalId: str | None = sqssubscription("QueuePolicyLogicalId") QueueUrl: SamIntrinsicable[str] = sqssubscription("QueueUrl") class SNSEventProperties(BaseModel): - FilterPolicy: Optional[PassThroughProp] = snseventproperties("FilterPolicy") - FilterPolicyScope: Optional[PassThroughProp] = passthrough_prop( + FilterPolicy: PassThroughProp | None = snseventproperties("FilterPolicy") + FilterPolicyScope: PassThroughProp | None = passthrough_prop( "sam-property-function-sns", "FilterPolicyScope", ["AWS::SNS::Subscription", "Properties", "FilterPolicyScope"], ) - Region: Optional[PassThroughProp] = snseventproperties("Region") - SqsSubscription: Optional[Union[bool, SqsSubscription]] = snseventproperties("SqsSubscription") + Region: PassThroughProp | None = snseventproperties("Region") + SqsSubscription: Union[bool, SqsSubscription] | None = snseventproperties("SqsSubscription") Topic: PassThroughProp = snseventproperties("Topic") @@ -163,31 +163,31 @@ class SNSEvent(BaseModel): class FunctionUrlConfig(BaseModel): AuthType: SamIntrinsicable[str] = functionurlconfig("AuthType") - Cors: Optional[PassThroughProp] = functionurlconfig("Cors") - InvokeMode: Optional[PassThroughProp] = functionurlconfig("InvokeMode") + Cors: PassThroughProp | None = functionurlconfig("Cors") + InvokeMode: PassThroughProp | None = functionurlconfig("InvokeMode") class KinesisEventProperties(BaseModel): - BatchSize: Optional[PassThroughProp] = kinesiseventproperties("BatchSize") - BisectBatchOnFunctionError: Optional[PassThroughProp] = kinesiseventproperties("BisectBatchOnFunctionError") - DestinationConfig: Optional[PassThroughProp] = kinesiseventproperties("DestinationConfig") - Enabled: Optional[PassThroughProp] = kinesiseventproperties("Enabled") - FilterCriteria: Optional[PassThroughProp] = kinesiseventproperties("FilterCriteria") - FunctionResponseTypes: Optional[PassThroughProp] = kinesiseventproperties("FunctionResponseTypes") - KmsKeyArn: Optional[PassThroughProp] = passthrough_prop( + BatchSize: PassThroughProp | None = kinesiseventproperties("BatchSize") + BisectBatchOnFunctionError: PassThroughProp | None = kinesiseventproperties("BisectBatchOnFunctionError") + DestinationConfig: PassThroughProp | None = kinesiseventproperties("DestinationConfig") + Enabled: PassThroughProp | None = kinesiseventproperties("Enabled") + FilterCriteria: PassThroughProp | None = kinesiseventproperties("FilterCriteria") + FunctionResponseTypes: PassThroughProp | None = kinesiseventproperties("FunctionResponseTypes") + KmsKeyArn: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "KmsKeyArn", ["AWS::Lambda::EventSourceMapping", "Properties", "KmsKeyArn"], ) - MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = kinesiseventproperties("MaximumBatchingWindowInSeconds") - MaximumRecordAgeInSeconds: Optional[PassThroughProp] = kinesiseventproperties("MaximumRecordAgeInSeconds") - MaximumRetryAttempts: Optional[PassThroughProp] = kinesiseventproperties("MaximumRetryAttempts") - ParallelizationFactor: Optional[PassThroughProp] = kinesiseventproperties("ParallelizationFactor") - StartingPosition: Optional[PassThroughProp] = kinesiseventproperties("StartingPosition") - StartingPositionTimestamp: Optional[PassThroughProp] = kinesiseventproperties("StartingPositionTimestamp") + MaximumBatchingWindowInSeconds: PassThroughProp | None = kinesiseventproperties("MaximumBatchingWindowInSeconds") + MaximumRecordAgeInSeconds: PassThroughProp | None = kinesiseventproperties("MaximumRecordAgeInSeconds") + MaximumRetryAttempts: PassThroughProp | None = kinesiseventproperties("MaximumRetryAttempts") + ParallelizationFactor: PassThroughProp | None = kinesiseventproperties("ParallelizationFactor") + StartingPosition: PassThroughProp | None = kinesiseventproperties("StartingPosition") + StartingPositionTimestamp: PassThroughProp | None = kinesiseventproperties("StartingPositionTimestamp") Stream: PassThroughProp = kinesiseventproperties("Stream") - TumblingWindowInSeconds: Optional[PassThroughProp] = kinesiseventproperties("TumblingWindowInSeconds") - MetricsConfig: Optional[PassThroughProp] + TumblingWindowInSeconds: PassThroughProp | None = kinesiseventproperties("TumblingWindowInSeconds") + MetricsConfig: PassThroughProp | None class KinesisEvent(BaseModel): @@ -196,28 +196,26 @@ class KinesisEvent(BaseModel): class DynamoDBEventProperties(BaseModel): - BatchSize: Optional[PassThroughProp] = dynamodbeventproperties("BatchSize") - BisectBatchOnFunctionError: Optional[PassThroughProp] = dynamodbeventproperties("BisectBatchOnFunctionError") - DestinationConfig: Optional[PassThroughProp] = dynamodbeventproperties("DestinationConfig") - Enabled: Optional[PassThroughProp] = dynamodbeventproperties("Enabled") - FilterCriteria: Optional[PassThroughProp] = dynamodbeventproperties("FilterCriteria") - FunctionResponseTypes: Optional[PassThroughProp] = dynamodbeventproperties("FunctionResponseTypes") - KmsKeyArn: Optional[PassThroughProp] = passthrough_prop( + BatchSize: PassThroughProp | None = dynamodbeventproperties("BatchSize") + BisectBatchOnFunctionError: PassThroughProp | None = dynamodbeventproperties("BisectBatchOnFunctionError") + DestinationConfig: PassThroughProp | None = dynamodbeventproperties("DestinationConfig") + Enabled: PassThroughProp | None = dynamodbeventproperties("Enabled") + FilterCriteria: PassThroughProp | None = dynamodbeventproperties("FilterCriteria") + FunctionResponseTypes: PassThroughProp | None = dynamodbeventproperties("FunctionResponseTypes") + KmsKeyArn: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "KmsKeyArn", ["AWS::Lambda::EventSourceMapping", "Properties", "KmsKeyArn"], ) - MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = dynamodbeventproperties( - "MaximumBatchingWindowInSeconds" - ) - MaximumRecordAgeInSeconds: Optional[PassThroughProp] = dynamodbeventproperties("MaximumRecordAgeInSeconds") - MaximumRetryAttempts: Optional[PassThroughProp] = dynamodbeventproperties("MaximumRetryAttempts") - ParallelizationFactor: Optional[PassThroughProp] = dynamodbeventproperties("ParallelizationFactor") - StartingPosition: Optional[PassThroughProp] = dynamodbeventproperties("StartingPosition") - StartingPositionTimestamp: Optional[PassThroughProp] = dynamodbeventproperties("StartingPositionTimestamp") + MaximumBatchingWindowInSeconds: PassThroughProp | None = dynamodbeventproperties("MaximumBatchingWindowInSeconds") + MaximumRecordAgeInSeconds: PassThroughProp | None = dynamodbeventproperties("MaximumRecordAgeInSeconds") + MaximumRetryAttempts: PassThroughProp | None = dynamodbeventproperties("MaximumRetryAttempts") + ParallelizationFactor: PassThroughProp | None = dynamodbeventproperties("ParallelizationFactor") + StartingPosition: PassThroughProp | None = dynamodbeventproperties("StartingPosition") + StartingPositionTimestamp: PassThroughProp | None = dynamodbeventproperties("StartingPositionTimestamp") Stream: PassThroughProp = dynamodbeventproperties("Stream") - TumblingWindowInSeconds: Optional[PassThroughProp] = dynamodbeventproperties("TumblingWindowInSeconds") - MetricsConfig: Optional[PassThroughProp] + TumblingWindowInSeconds: PassThroughProp | None = dynamodbeventproperties("TumblingWindowInSeconds") + MetricsConfig: PassThroughProp | None class DynamoDBEvent(BaseModel): @@ -226,25 +224,23 @@ class DynamoDBEvent(BaseModel): class DocumentDBEventProperties(BaseModel): - BatchSize: Optional[PassThroughProp] = documentdbeventproperties("BatchSize") + BatchSize: PassThroughProp | None = documentdbeventproperties("BatchSize") Cluster: PassThroughProp = documentdbeventproperties("Cluster") - CollectionName: Optional[PassThroughProp] = documentdbeventproperties("CollectionName") + CollectionName: PassThroughProp | None = documentdbeventproperties("CollectionName") DatabaseName: PassThroughProp = documentdbeventproperties("DatabaseName") - Enabled: Optional[PassThroughProp] = documentdbeventproperties("Enabled") - FilterCriteria: Optional[PassThroughProp] = documentdbeventproperties("FilterCriteria") - FullDocument: Optional[PassThroughProp] = documentdbeventproperties("FullDocument") - KmsKeyArn: Optional[PassThroughProp] = passthrough_prop( + Enabled: PassThroughProp | None = documentdbeventproperties("Enabled") + FilterCriteria: PassThroughProp | None = documentdbeventproperties("FilterCriteria") + FullDocument: PassThroughProp | None = documentdbeventproperties("FullDocument") + KmsKeyArn: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "KmsKeyArn", ["AWS::Lambda::EventSourceMapping", "Properties", "KmsKeyArn"], ) - MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = documentdbeventproperties( - "MaximumBatchingWindowInSeconds" - ) - SecretsManagerKmsKeyId: Optional[str] = documentdbeventproperties("SecretsManagerKmsKeyId") + MaximumBatchingWindowInSeconds: PassThroughProp | None = documentdbeventproperties("MaximumBatchingWindowInSeconds") + SecretsManagerKmsKeyId: str | None = documentdbeventproperties("SecretsManagerKmsKeyId") SourceAccessConfigurations: PassThroughProp = documentdbeventproperties("SourceAccessConfigurations") - StartingPosition: Optional[PassThroughProp] = documentdbeventproperties("StartingPosition") - StartingPositionTimestamp: Optional[PassThroughProp] = documentdbeventproperties("StartingPositionTimestamp") + StartingPosition: PassThroughProp | None = documentdbeventproperties("StartingPosition") + StartingPositionTimestamp: PassThroughProp | None = documentdbeventproperties("StartingPositionTimestamp") class DocumentDBEvent(BaseModel): @@ -253,15 +249,15 @@ class DocumentDBEvent(BaseModel): class SQSEventProperties(BaseModel): - BatchSize: Optional[PassThroughProp] = sqseventproperties("BatchSize") - Enabled: Optional[PassThroughProp] = sqseventproperties("Enabled") - FilterCriteria: Optional[PassThroughProp] = sqseventproperties("FilterCriteria") - FunctionResponseTypes: Optional[PassThroughProp] = sqseventproperties("FunctionResponseTypes") - KmsKeyArn: Optional[PassThroughProp] = sqseventproperties("KmsKeyArn") - MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = sqseventproperties("MaximumBatchingWindowInSeconds") + BatchSize: PassThroughProp | None = sqseventproperties("BatchSize") + Enabled: PassThroughProp | None = sqseventproperties("Enabled") + FilterCriteria: PassThroughProp | None = sqseventproperties("FilterCriteria") + FunctionResponseTypes: PassThroughProp | None = sqseventproperties("FunctionResponseTypes") + KmsKeyArn: PassThroughProp | None = sqseventproperties("KmsKeyArn") + MaximumBatchingWindowInSeconds: PassThroughProp | None = sqseventproperties("MaximumBatchingWindowInSeconds") Queue: PassThroughProp = sqseventproperties("Queue") - ScalingConfig: Optional[PassThroughProp] # Update docs when live - MetricsConfig: Optional[PassThroughProp] + ScalingConfig: PassThroughProp | None # Update docs when live + MetricsConfig: PassThroughProp | None class SQSEvent(BaseModel): @@ -270,45 +266,45 @@ class SQSEvent(BaseModel): class ApiAuth(BaseModel): - ApiKeyRequired: Optional[bool] = apiauth("ApiKeyRequired") - AuthorizationScopes: Optional[List[str]] = apiauth("AuthorizationScopes") - Authorizer: Optional[str] = apiauth("Authorizer") - InvokeRole: Optional[SamIntrinsicable[str]] = apiauth("InvokeRole") - ResourcePolicy: Optional[ResourcePolicy] = apiauth("ResourcePolicy") + ApiKeyRequired: bool | None = apiauth("ApiKeyRequired") + AuthorizationScopes: list[str] | None = apiauth("AuthorizationScopes") + Authorizer: str | None = apiauth("Authorizer") + InvokeRole: SamIntrinsicable[str] | None = apiauth("InvokeRole") + ResourcePolicy: ResourcePolicy | None = apiauth("ResourcePolicy") # TODO explicitly mention in docs that intrinsics are not supported for OverrideApiAuth - OverrideApiAuth: Optional[bool] = apiauth("OverrideApiAuth") + OverrideApiAuth: bool | None = apiauth("OverrideApiAuth") class RequestModel(BaseModel): Model: str = requestmodel("Model") - Required: Optional[bool] = requestmodel("Required") - ValidateBody: Optional[bool] = requestmodel("ValidateBody") - ValidateParameters: Optional[bool] = requestmodel("ValidateParameters") + Required: bool | None = requestmodel("Required") + ValidateBody: bool | None = requestmodel("ValidateBody") + ValidateParameters: bool | None = requestmodel("ValidateParameters") class RequestParameters(BaseModel): - Caching: Optional[bool] = requestparameters("Caching") - Required: Optional[bool] = requestparameters("Required") + Caching: bool | None = requestparameters("Caching") + Required: bool | None = requestparameters("Required") # TODO: docs says either str or RequestParameter but implementation is an array of str or RequestParameter # remove this comment once updated documentation -RequestModelProperty = List[Union[str, Dict[str, RequestParameters]]] +RequestModelProperty = list[Union[str, dict[str, RequestParameters]]] class ApiEventProperties(BaseModel): - Auth: Optional[ApiAuth] = apieventproperties("Auth") + Auth: ApiAuth | None = apieventproperties("Auth") Method: str = apieventproperties("Method") Path: str = apieventproperties("Path") - RequestModel: Optional[RequestModel] = apieventproperties("RequestModel") - RequestParameters: Optional[RequestModelProperty] = apieventproperties("RequestParameters") - RestApiId: Optional[Union[str, Ref]] = apieventproperties("RestApiId") - TimeoutInMillis: Optional[PassThroughProp] = passthrough_prop( + RequestModel: RequestModel | None = apieventproperties("RequestModel") + RequestParameters: RequestModelProperty | None = apieventproperties("RequestParameters") + RestApiId: Union[str, Ref] | None = apieventproperties("RestApiId") + TimeoutInMillis: PassThroughProp | None = passthrough_prop( "sam-property-function-api", "TimeoutInMillis", ["AWS::ApiGateway::Method.Integration", "TimeoutInMillis"], ) - ResponseTransferMode: Optional[PassThroughProp] = apieventproperties("ResponseTransferMode") + ResponseTransferMode: PassThroughProp | None = apieventproperties("ResponseTransferMode") class ApiEvent(BaseModel): @@ -317,12 +313,12 @@ class ApiEvent(BaseModel): class CloudWatchEventProperties(BaseModel): - Enabled: Optional[bool] = cloudwatcheventproperties("Enabled") - EventBusName: Optional[PassThroughProp] = cloudwatcheventproperties("EventBusName") - Input: Optional[PassThroughProp] = cloudwatcheventproperties("Input") - InputPath: Optional[PassThroughProp] = cloudwatcheventproperties("InputPath") - Pattern: Optional[PassThroughProp] = cloudwatcheventproperties("Pattern") - State: Optional[PassThroughProp] = cloudwatcheventproperties("State") + Enabled: bool | None = cloudwatcheventproperties("Enabled") + EventBusName: PassThroughProp | None = cloudwatcheventproperties("EventBusName") + Input: PassThroughProp | None = cloudwatcheventproperties("Input") + InputPath: PassThroughProp | None = cloudwatcheventproperties("InputPath") + Pattern: PassThroughProp | None = cloudwatcheventproperties("Pattern") + State: PassThroughProp | None = cloudwatcheventproperties("State") class CloudWatchEvent(BaseModel): @@ -331,20 +327,20 @@ class CloudWatchEvent(BaseModel): class DeadLetterConfig(BaseModel): - Arn: Optional[PassThroughProp] = deadletterconfig("Arn") - QueueLogicalId: Optional[str] = deadletterconfig("QueueLogicalId") - Type: Optional[Literal["SQS"]] = deadletterconfig("Type") + Arn: PassThroughProp | None = deadletterconfig("Arn") + QueueLogicalId: str | None = deadletterconfig("QueueLogicalId") + Type: Literal["SQS"] | None = deadletterconfig("Type") class EventsScheduleProperties(BaseModel): - DeadLetterConfig: Optional[DeadLetterConfig] = eventsscheduleproperties("DeadLetterConfig") - Description: Optional[PassThroughProp] = eventsscheduleproperties("Description") - Enabled: Optional[bool] = eventsscheduleproperties("Enabled") - Input: Optional[PassThroughProp] = eventsscheduleproperties("Input") - Name: Optional[PassThroughProp] = eventsscheduleproperties("Name") - RetryPolicy: Optional[PassThroughProp] = eventsscheduleproperties("RetryPolicy") - Schedule: Optional[PassThroughProp] = eventsscheduleproperties("Schedule") - State: Optional[PassThroughProp] = eventsscheduleproperties("State") + DeadLetterConfig: DeadLetterConfig | None = eventsscheduleproperties("DeadLetterConfig") + Description: PassThroughProp | None = eventsscheduleproperties("Description") + Enabled: bool | None = eventsscheduleproperties("Enabled") + Input: PassThroughProp | None = eventsscheduleproperties("Input") + Name: PassThroughProp | None = eventsscheduleproperties("Name") + RetryPolicy: PassThroughProp | None = eventsscheduleproperties("RetryPolicy") + Schedule: PassThroughProp | None = eventsscheduleproperties("Schedule") + State: PassThroughProp | None = eventsscheduleproperties("State") class ScheduleEvent(BaseModel): @@ -357,15 +353,15 @@ class EventBridgeRuleTarget(BaseModel): class EventBridgeRuleEventProperties(BaseModel): - DeadLetterConfig: Optional[DeadLetterConfig] = eventbridgeruleeventproperties("DeadLetterConfig") - EventBusName: Optional[PassThroughProp] = eventbridgeruleeventproperties("EventBusName") - Input: Optional[PassThroughProp] = eventbridgeruleeventproperties("Input") - InputPath: Optional[PassThroughProp] = eventbridgeruleeventproperties("InputPath") + DeadLetterConfig: DeadLetterConfig | None = eventbridgeruleeventproperties("DeadLetterConfig") + EventBusName: PassThroughProp | None = eventbridgeruleeventproperties("EventBusName") + Input: PassThroughProp | None = eventbridgeruleeventproperties("Input") + InputPath: PassThroughProp | None = eventbridgeruleeventproperties("InputPath") Pattern: PassThroughProp = eventbridgeruleeventproperties("Pattern") - RetryPolicy: Optional[PassThroughProp] = eventbridgeruleeventproperties("RetryPolicy") - Target: Optional[EventBridgeRuleTarget] = eventbridgeruleeventproperties("Target") - InputTransformer: Optional[PassThroughProp] = eventbridgeruleeventproperties("InputTransformer") - RuleName: Optional[PassThroughProp] = eventbridgeruleeventproperties("RuleName") + RetryPolicy: PassThroughProp | None = eventbridgeruleeventproperties("RetryPolicy") + Target: EventBridgeRuleTarget | None = eventbridgeruleeventproperties("Target") + InputTransformer: PassThroughProp | None = eventbridgeruleeventproperties("InputTransformer") + RuleName: PassThroughProp | None = eventbridgeruleeventproperties("RuleName") class EventBridgeRuleEvent(BaseModel): @@ -384,7 +380,7 @@ class CloudWatchLogsEvent(BaseModel): class IoTRuleEventProperties(BaseModel): - AwsIotSqlVersion: Optional[PassThroughProp] = iotruleeventproperties("AwsIotSqlVersion") + AwsIotSqlVersion: PassThroughProp | None = iotruleeventproperties("AwsIotSqlVersion") Sql: PassThroughProp = iotruleeventproperties("Sql") @@ -394,12 +390,12 @@ class IoTRuleEvent(BaseModel): class AlexaSkillEventProperties(BaseModel): - SkillId: Optional[str] = alexaskilleventproperties("SkillId") + SkillId: str | None = alexaskilleventproperties("SkillId") class AlexaSkillEvent(BaseModel): Type: Literal["AlexaSkill"] = event("Type") - Properties: Optional[AlexaSkillEventProperties] = event("Properties") + Properties: AlexaSkillEventProperties | None = event("Properties") class CognitoEventProperties(BaseModel): @@ -413,58 +409,58 @@ class CognitoEvent(BaseModel): class HttpApiAuth(BaseModel): - AuthorizationScopes: Optional[List[str]] = httpapiauth("AuthorizationScopes") - Authorizer: Optional[str] = httpapiauth("Authorizer") + AuthorizationScopes: list[str] | None = httpapiauth("AuthorizationScopes") + Authorizer: str | None = httpapiauth("Authorizer") class HttpApiEventProperties(BaseModel): - ApiId: Optional[SamIntrinsicable[str]] = httpapieventproperties("ApiId") - Auth: Optional[HttpApiAuth] = httpapieventproperties("Auth") - Method: Optional[str] = httpapieventproperties("Method") - Path: Optional[str] = httpapieventproperties("Path") - PayloadFormatVersion: Optional[SamIntrinsicable[str]] = httpapieventproperties("PayloadFormatVersion") - RouteSettings: Optional[PassThroughProp] = httpapieventproperties("RouteSettings") - TimeoutInMillis: Optional[SamIntrinsicable[int]] = httpapieventproperties("TimeoutInMillis") + ApiId: SamIntrinsicable[str] | None = httpapieventproperties("ApiId") + Auth: HttpApiAuth | None = httpapieventproperties("Auth") + Method: str | None = httpapieventproperties("Method") + Path: str | None = httpapieventproperties("Path") + PayloadFormatVersion: SamIntrinsicable[str] | None = httpapieventproperties("PayloadFormatVersion") + RouteSettings: PassThroughProp | None = httpapieventproperties("RouteSettings") + TimeoutInMillis: SamIntrinsicable[int] | None = httpapieventproperties("TimeoutInMillis") class HttpApiEvent(BaseModel): Type: Literal["HttpApi"] = event("Type") - Properties: Optional[HttpApiEventProperties] = event("Properties") + Properties: HttpApiEventProperties | None = event("Properties") class MSKEventProperties(BaseModel): - BatchSize: Optional[PassThroughProp] = passthrough_prop( + BatchSize: PassThroughProp | None = passthrough_prop( "sam-property-function-msk", "BatchSize", ["AWS::Lambda::EventSourceMapping", "Properties", "BatchSize"], ) - ConsumerGroupId: Optional[PassThroughProp] = mskeventproperties("ConsumerGroupId") - Enabled: Optional[PassThroughProp] = passthrough_prop( + ConsumerGroupId: PassThroughProp | None = mskeventproperties("ConsumerGroupId") + Enabled: PassThroughProp | None = passthrough_prop( "sam-property-function-msk", "Enabled", ["AWS::Lambda::EventSourceMapping", "Properties", "Enabled"], ) - FilterCriteria: Optional[PassThroughProp] = mskeventproperties("FilterCriteria") - KmsKeyArn: Optional[PassThroughProp] = mskeventproperties("KmsKeyArn") - MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = mskeventproperties("MaximumBatchingWindowInSeconds") - StartingPosition: Optional[PassThroughProp] = mskeventproperties("StartingPosition") - StartingPositionTimestamp: Optional[PassThroughProp] = mskeventproperties("StartingPositionTimestamp") + FilterCriteria: PassThroughProp | None = mskeventproperties("FilterCriteria") + KmsKeyArn: PassThroughProp | None = mskeventproperties("KmsKeyArn") + MaximumBatchingWindowInSeconds: PassThroughProp | None = mskeventproperties("MaximumBatchingWindowInSeconds") + StartingPosition: PassThroughProp | None = mskeventproperties("StartingPosition") + StartingPositionTimestamp: PassThroughProp | None = mskeventproperties("StartingPositionTimestamp") Stream: PassThroughProp = mskeventproperties("Stream") Topics: PassThroughProp = mskeventproperties("Topics") - SourceAccessConfigurations: Optional[PassThroughProp] = mskeventproperties("SourceAccessConfigurations") - DestinationConfig: Optional[PassThroughProp] = passthrough_prop( + SourceAccessConfigurations: PassThroughProp | None = mskeventproperties("SourceAccessConfigurations") + DestinationConfig: PassThroughProp | None = passthrough_prop( "sam-property-function-msk", "DestinationConfig", ["AWS::Lambda::EventSourceMapping", "Properties", "DestinationConfig"], ) - ProvisionedPollerConfig: Optional[PassThroughProp] = mskeventproperties("ProvisionedPollerConfig") - SchemaRegistryConfig: Optional[PassThroughProp] = mskeventproperties("SchemaRegistryConfig") - MetricsConfig: Optional[PassThroughProp] = mskeventproperties("MetricsConfig") - LoggingConfig: Optional[PassThroughProp] = mskeventproperties("LoggingConfig") - BisectBatchOnFunctionError: Optional[PassThroughProp] = mskeventproperties("BisectBatchOnFunctionError") - FunctionResponseTypes: Optional[PassThroughProp] = mskeventproperties("FunctionResponseTypes") - MaximumRecordAgeInSeconds: Optional[PassThroughProp] = mskeventproperties("MaximumRecordAgeInSeconds") - MaximumRetryAttempts: Optional[PassThroughProp] = mskeventproperties("MaximumRetryAttempts") + ProvisionedPollerConfig: PassThroughProp | None = mskeventproperties("ProvisionedPollerConfig") + SchemaRegistryConfig: PassThroughProp | None = mskeventproperties("SchemaRegistryConfig") + MetricsConfig: PassThroughProp | None = mskeventproperties("MetricsConfig") + LoggingConfig: PassThroughProp | None = mskeventproperties("LoggingConfig") + BisectBatchOnFunctionError: PassThroughProp | None = mskeventproperties("BisectBatchOnFunctionError") + FunctionResponseTypes: PassThroughProp | None = mskeventproperties("FunctionResponseTypes") + MaximumRecordAgeInSeconds: PassThroughProp | None = mskeventproperties("MaximumRecordAgeInSeconds") + MaximumRetryAttempts: PassThroughProp | None = mskeventproperties("MaximumRetryAttempts") class MSKEvent(BaseModel): @@ -473,19 +469,19 @@ class MSKEvent(BaseModel): class MQEventProperties(BaseModel): - BatchSize: Optional[PassThroughProp] = mqeventproperties("BatchSize") + BatchSize: PassThroughProp | None = mqeventproperties("BatchSize") Broker: PassThroughProp = mqeventproperties("Broker") - DynamicPolicyName: Optional[bool] = mqeventproperties("DynamicPolicyName") - Enabled: Optional[PassThroughProp] = mqeventproperties("Enabled") - FilterCriteria: Optional[PassThroughProp] = mqeventproperties("FilterCriteria") - KmsKeyArn: Optional[PassThroughProp] = passthrough_prop( + DynamicPolicyName: bool | None = mqeventproperties("DynamicPolicyName") + Enabled: PassThroughProp | None = mqeventproperties("Enabled") + FilterCriteria: PassThroughProp | None = mqeventproperties("FilterCriteria") + KmsKeyArn: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "KmsKeyArn", ["AWS::Lambda::EventSourceMapping", "Properties", "KmsKeyArn"], ) - MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = mqeventproperties("MaximumBatchingWindowInSeconds") + MaximumBatchingWindowInSeconds: PassThroughProp | None = mqeventproperties("MaximumBatchingWindowInSeconds") Queues: PassThroughProp = mqeventproperties("Queues") - SecretsManagerKmsKeyId: Optional[str] = mqeventproperties("SecretsManagerKmsKeyId") + SecretsManagerKmsKeyId: str | None = mqeventproperties("SecretsManagerKmsKeyId") SourceAccessConfigurations: PassThroughProp = mqeventproperties("SourceAccessConfigurations") @@ -495,32 +491,28 @@ class MQEvent(BaseModel): class SelfManagedKafkaEventProperties(BaseModel): - BatchSize: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("BatchSize") - ConsumerGroupId: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("ConsumerGroupId") - Enabled: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("Enabled") - FilterCriteria: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("FilterCriteria") - KafkaBootstrapServers: Optional[List[SamIntrinsicable[str]]] = selfmanagedkafkaeventproperties( - "KafkaBootstrapServers" - ) - KmsKeyArn: Optional[PassThroughProp] = passthrough_prop( + BatchSize: PassThroughProp | None = selfmanagedkafkaeventproperties("BatchSize") + ConsumerGroupId: PassThroughProp | None = selfmanagedkafkaeventproperties("ConsumerGroupId") + Enabled: PassThroughProp | None = selfmanagedkafkaeventproperties("Enabled") + FilterCriteria: PassThroughProp | None = selfmanagedkafkaeventproperties("FilterCriteria") + KafkaBootstrapServers: list[SamIntrinsicable[str]] | None = selfmanagedkafkaeventproperties("KafkaBootstrapServers") + KmsKeyArn: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "KmsKeyArn", ["AWS::Lambda::EventSourceMapping", "Properties", "KmsKeyArn"], ) SourceAccessConfigurations: PassThroughProp = selfmanagedkafkaeventproperties("SourceAccessConfigurations") - StartingPosition: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("StartingPosition") - StartingPositionTimestamp: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("StartingPositionTimestamp") + StartingPosition: PassThroughProp | None = selfmanagedkafkaeventproperties("StartingPosition") + StartingPositionTimestamp: PassThroughProp | None = selfmanagedkafkaeventproperties("StartingPositionTimestamp") Topics: PassThroughProp = selfmanagedkafkaeventproperties("Topics") - MetricsConfig: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("MetricsConfig") - ProvisionedPollerConfig: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("ProvisionedPollerConfig") - SchemaRegistryConfig: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("SchemaRegistryConfig") - LoggingConfig: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("LoggingConfig") - BisectBatchOnFunctionError: Optional[PassThroughProp] = selfmanagedkafkaeventproperties( - "BisectBatchOnFunctionError" - ) - MaximumRecordAgeInSeconds: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("MaximumRecordAgeInSeconds") - MaximumRetryAttempts: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("MaximumRetryAttempts") - FunctionResponseTypes: Optional[PassThroughProp] = selfmanagedkafkaeventproperties("FunctionResponseTypes") + MetricsConfig: PassThroughProp | None = selfmanagedkafkaeventproperties("MetricsConfig") + ProvisionedPollerConfig: PassThroughProp | None = selfmanagedkafkaeventproperties("ProvisionedPollerConfig") + SchemaRegistryConfig: PassThroughProp | None = selfmanagedkafkaeventproperties("SchemaRegistryConfig") + LoggingConfig: PassThroughProp | None = selfmanagedkafkaeventproperties("LoggingConfig") + BisectBatchOnFunctionError: PassThroughProp | None = selfmanagedkafkaeventproperties("BisectBatchOnFunctionError") + MaximumRecordAgeInSeconds: PassThroughProp | None = selfmanagedkafkaeventproperties("MaximumRecordAgeInSeconds") + MaximumRetryAttempts: PassThroughProp | None = selfmanagedkafkaeventproperties("MaximumRetryAttempts") + FunctionResponseTypes: PassThroughProp | None = selfmanagedkafkaeventproperties("FunctionResponseTypes") class SelfManagedKafkaEvent(BaseModel): @@ -530,23 +522,23 @@ class SelfManagedKafkaEvent(BaseModel): # TODO: Same as ScheduleV2EventProperties in state machine? class ScheduleV2EventProperties(BaseModel): - DeadLetterConfig: Optional[DeadLetterConfig] = schedulev2eventproperties("DeadLetterConfig") - Description: Optional[PassThroughProp] = schedulev2eventproperties("Description") - EndDate: Optional[PassThroughProp] = schedulev2eventproperties("EndDate") - FlexibleTimeWindow: Optional[PassThroughProp] = schedulev2eventproperties("FlexibleTimeWindow") - GroupName: Optional[PassThroughProp] = schedulev2eventproperties("GroupName") - Input: Optional[PassThroughProp] = schedulev2eventproperties("Input") - KmsKeyArn: Optional[PassThroughProp] = schedulev2eventproperties("KmsKeyArn") - Name: Optional[PassThroughProp] = schedulev2eventproperties("Name") - PermissionsBoundary: Optional[PassThroughProp] = schedulev2eventproperties("PermissionsBoundary") - RetryPolicy: Optional[PassThroughProp] = schedulev2eventproperties("RetryPolicy") - RoleArn: Optional[PassThroughProp] = schedulev2eventproperties("RoleArn") - ScheduleExpression: Optional[PassThroughProp] = schedulev2eventproperties("ScheduleExpression") - ScheduleExpressionTimezone: Optional[PassThroughProp] = schedulev2eventproperties("ScheduleExpressionTimezone") - StartDate: Optional[PassThroughProp] = schedulev2eventproperties("StartDate") - State: Optional[PassThroughProp] = schedulev2eventproperties("State") + DeadLetterConfig: DeadLetterConfig | None = schedulev2eventproperties("DeadLetterConfig") + Description: PassThroughProp | None = schedulev2eventproperties("Description") + EndDate: PassThroughProp | None = schedulev2eventproperties("EndDate") + FlexibleTimeWindow: PassThroughProp | None = schedulev2eventproperties("FlexibleTimeWindow") + GroupName: PassThroughProp | None = schedulev2eventproperties("GroupName") + Input: PassThroughProp | None = schedulev2eventproperties("Input") + KmsKeyArn: PassThroughProp | None = schedulev2eventproperties("KmsKeyArn") + Name: PassThroughProp | None = schedulev2eventproperties("Name") + PermissionsBoundary: PassThroughProp | None = schedulev2eventproperties("PermissionsBoundary") + RetryPolicy: PassThroughProp | None = schedulev2eventproperties("RetryPolicy") + RoleArn: PassThroughProp | None = schedulev2eventproperties("RoleArn") + ScheduleExpression: PassThroughProp | None = schedulev2eventproperties("ScheduleExpression") + ScheduleExpressionTimezone: PassThroughProp | None = schedulev2eventproperties("ScheduleExpressionTimezone") + StartDate: PassThroughProp | None = schedulev2eventproperties("StartDate") + State: PassThroughProp | None = schedulev2eventproperties("State") # OmitName is a SAM-specific boolean property, not a CloudFormation pass-through property - OmitName: Optional[bool] + OmitName: bool | None class ScheduleV2Event(BaseModel): @@ -554,83 +546,83 @@ class ScheduleV2Event(BaseModel): Properties: ScheduleV2EventProperties = event("Properties") -Handler = Optional[PassThroughProp] -Runtime = Optional[PassThroughProp] -CodeUriType = Optional[Union[str, CodeUri]] -DeadLetterQueueType = Optional[SamIntrinsicable[DeadLetterQueue]] -Description = Optional[PassThroughProp] -MemorySize = Optional[PassThroughProp] -Timeout = Optional[PassThroughProp] -VpcConfig = Optional[PassThroughProp] -Environment = Optional[PassThroughProp] -Tags = Optional[DictStrAny] -Tracing = Optional[SamIntrinsicable[Literal["Active", "PassThrough", "Disabled"]]] -KmsKeyArn = Optional[PassThroughProp] -Layers = Optional[PassThroughProp] -AutoPublishAlias = Optional[SamIntrinsicable[str]] -AutoPublishAliasAllProperties = Optional[bool] -RolePath = Optional[PassThroughProp] -PermissionsBoundary = Optional[PassThroughProp] -ReservedConcurrentExecutions = Optional[PassThroughProp] -ProvisionedConcurrencyConfig = Optional[PassThroughProp] -AssumeRolePolicyDocument = Optional[DictStrAny] -Architectures = Optional[PassThroughProp] -EphemeralStorage = Optional[PassThroughProp] -SnapStart = Optional[PassThroughProp] # TODO: check the type -RuntimeManagementConfig = Optional[PassThroughProp] # TODO: check the type -LoggingConfig = Optional[PassThroughProp] # Type alias - documentation added to Properties and Globals classes -RecursiveLoop = Optional[PassThroughProp] -SourceKMSKeyArn = Optional[PassThroughProp] -TenancyConfig = Optional[PassThroughProp] +Handler = PassThroughProp | None +Runtime = PassThroughProp | None +CodeUriType = Union[str, CodeUri] | None +DeadLetterQueueType = SamIntrinsicable[DeadLetterQueue] | None +Description = PassThroughProp | None +MemorySize = PassThroughProp | None +Timeout = PassThroughProp | None +VpcConfig = PassThroughProp | None +Environment = PassThroughProp | None +Tags = DictStrAny | None +Tracing = SamIntrinsicable[Literal["Active", "PassThrough", "Disabled"]] | None +KmsKeyArn = PassThroughProp | None +Layers = PassThroughProp | None +AutoPublishAlias = SamIntrinsicable[str] | None +AutoPublishAliasAllProperties = bool | None +RolePath = PassThroughProp | None +PermissionsBoundary = PassThroughProp | None +ReservedConcurrentExecutions = PassThroughProp | None +ProvisionedConcurrencyConfig = PassThroughProp | None +AssumeRolePolicyDocument = DictStrAny | None +Architectures = PassThroughProp | None +EphemeralStorage = PassThroughProp | None +SnapStart = PassThroughProp | None # TODO: check the type +RuntimeManagementConfig = PassThroughProp | None # TODO: check the type +LoggingConfig = PassThroughProp | None # Type alias - documentation added to Properties and Globals classes +RecursiveLoop = PassThroughProp | None +SourceKMSKeyArn = PassThroughProp | None +TenancyConfig = PassThroughProp | None class CapacityProviderConfig(BaseModel): Arn: SamIntrinsicable[str] = capacityproviderconfig("Arn") - PerExecutionEnvironmentMaxConcurrency: Optional[SamIntrinsicable[int]] = capacityproviderconfig( + PerExecutionEnvironmentMaxConcurrency: SamIntrinsicable[int] | None = capacityproviderconfig( "PerExecutionEnvironmentMaxConcurrency" ) - ExecutionEnvironmentMemoryGiBPerVCpu: Optional[SamIntrinsicable[Union[int, float]]] = capacityproviderconfig( + ExecutionEnvironmentMemoryGiBPerVCpu: SamIntrinsicable[Union[int, float]] | None = capacityproviderconfig( "ExecutionEnvironmentMemoryGiBPerVCpu" ) class Properties(BaseModel): - Architectures: Optional[Architectures] = passthrough_prop( + Architectures: Architectures | None = passthrough_prop( PROPERTIES_STEM, "Architectures", ["AWS::Lambda::Function", "Properties", "Architectures"], ) - AssumeRolePolicyDocument: Optional[AssumeRolePolicyDocument] = prop("AssumeRolePolicyDocument") - AutoPublishAlias: Optional[AutoPublishAlias] = prop("AutoPublishAlias") - AutoPublishAliasAllProperties: Optional[AutoPublishAliasAllProperties] = prop("AutoPublishAliasAllProperties") - AutoPublishCodeSha256: Optional[SamIntrinsicable[str]] = prop("AutoPublishCodeSha256") - CodeSigningConfigArn: Optional[SamIntrinsicable[str]] = passthrough_prop( + AssumeRolePolicyDocument: AssumeRolePolicyDocument | None = prop("AssumeRolePolicyDocument") + AutoPublishAlias: AutoPublishAlias | None = prop("AutoPublishAlias") + AutoPublishAliasAllProperties: AutoPublishAliasAllProperties | None = prop("AutoPublishAliasAllProperties") + AutoPublishCodeSha256: SamIntrinsicable[str] | None = prop("AutoPublishCodeSha256") + CodeSigningConfigArn: SamIntrinsicable[str] | None = passthrough_prop( PROPERTIES_STEM, "CodeSigningConfigArn", ["AWS::Lambda::Function", "Properties", "CodeSigningConfigArn"], ) - CodeUri: Optional[CodeUriType] = prop("CodeUri") - DeadLetterQueue: Optional[DeadLetterQueueType] = prop("DeadLetterQueue") - DeploymentPreference: Optional[DeploymentPreference] = prop("DeploymentPreference") - Description: Optional[Description] = passthrough_prop( + CodeUri: CodeUriType | None = prop("CodeUri") + DeadLetterQueue: DeadLetterQueueType | None = prop("DeadLetterQueue") + DeploymentPreference: DeploymentPreference | None = prop("DeploymentPreference") + Description: Description | None = passthrough_prop( PROPERTIES_STEM, "Description", ["AWS::Lambda::Function", "Properties", "Description"], ) # TODO: Make the notation shorter; resource type and SAM/CFN property names usually same - Environment: Optional[Environment] = passthrough_prop( + Environment: Environment | None = passthrough_prop( PROPERTIES_STEM, "Environment", ["AWS::Lambda::Function", "Properties", "Environment"], ) - EphemeralStorage: Optional[EphemeralStorage] = passthrough_prop( + EphemeralStorage: EphemeralStorage | None = passthrough_prop( PROPERTIES_STEM, "EphemeralStorage", ["AWS::Lambda::Function", "Properties", "EphemeralStorage"], ) - EventInvokeConfig: Optional[EventInvokeConfig] = prop("EventInvokeConfig") - Events: Optional[ - Dict[ + EventInvokeConfig: EventInvokeConfig | None = prop("EventInvokeConfig") + Events: ( + dict[ str, Union[ S3Event, @@ -654,102 +646,103 @@ class Properties(BaseModel): SelfManagedKafkaEvent, ], ] - ] = prop("Events") - FileSystemConfigs: Optional[PassThroughProp] = passthrough_prop( + | None + ) = prop("Events") + FileSystemConfigs: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "FileSystemConfigs", ["AWS::Lambda::Function", "Properties", "FileSystemConfigs"], ) - FunctionName: Optional[PassThroughProp] = passthrough_prop( + FunctionName: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "FunctionName", ["AWS::Lambda::Function", "Properties", "FunctionName"], ) - FunctionUrlConfig: Optional[FunctionUrlConfig] = prop("FunctionUrlConfig") - Handler: Optional[Handler] = passthrough_prop( + FunctionUrlConfig: FunctionUrlConfig | None = prop("FunctionUrlConfig") + Handler: Handler | None = passthrough_prop( PROPERTIES_STEM, "Handler", ["AWS::Lambda::Function", "Properties", "Handler"], ) - ImageConfig: Optional[PassThroughProp] = passthrough_prop( + ImageConfig: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "ImageConfig", ["AWS::Lambda::Function", "Properties", "ImageConfig"], ) - ImageUri: Optional[PassThroughProp] = passthrough_prop( + ImageUri: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "ImageUri", ["AWS::Lambda::Function.Code", "ImageUri"], ) - InlineCode: Optional[PassThroughProp] = prop("InlineCode") - KmsKeyArn: Optional[KmsKeyArn] = prop("KmsKeyArn") - Layers: Optional[Layers] = prop("Layers") - MemorySize: Optional[MemorySize] = prop("MemorySize") - PackageType: Optional[PassThroughProp] = prop("PackageType") - RolePath: Optional[RolePath] = passthrough_prop( + InlineCode: PassThroughProp | None = prop("InlineCode") + KmsKeyArn: KmsKeyArn | None = prop("KmsKeyArn") + Layers: Layers | None = prop("Layers") + MemorySize: MemorySize | None = prop("MemorySize") + PackageType: PassThroughProp | None = prop("PackageType") + RolePath: RolePath | None = passthrough_prop( PROPERTIES_STEM, "RolePath", ["AWS::IAM::Role", "Properties", "Path"], ) - PermissionsBoundary: Optional[PermissionsBoundary] = passthrough_prop( + PermissionsBoundary: PermissionsBoundary | None = passthrough_prop( PROPERTIES_STEM, "PermissionsBoundary", ["AWS::IAM::Role", "Properties", "PermissionsBoundary"], ) - Policies: Optional[Union[str, DictStrAny, List[Union[str, DictStrAny]]]] = prop("Policies") - ProvisionedConcurrencyConfig: Optional[ProvisionedConcurrencyConfig] = passthrough_prop( + Policies: Union[str, DictStrAny, list[Union[str, DictStrAny]]] | None = prop("Policies") + ProvisionedConcurrencyConfig: ProvisionedConcurrencyConfig | None = passthrough_prop( PROPERTIES_STEM, "ProvisionedConcurrencyConfig", ["AWS::Lambda::Alias", "Properties", "ProvisionedConcurrencyConfig"], ) - ReservedConcurrentExecutions: Optional[ReservedConcurrentExecutions] = prop("ReservedConcurrentExecutions") - Role: Optional[SamIntrinsicable[str]] = prop("Role") - Runtime: Optional[Runtime] = passthrough_prop( + ReservedConcurrentExecutions: ReservedConcurrentExecutions | None = prop("ReservedConcurrentExecutions") + Role: SamIntrinsicable[str] | None = prop("Role") + Runtime: Runtime | None = passthrough_prop( PROPERTIES_STEM, "Runtime", ["AWS::Lambda::Function", "Properties", "Runtime"], ) - SnapStart: Optional[SnapStart] = prop("SnapStart") - RuntimeManagementConfig: Optional[RuntimeManagementConfig] = prop("RuntimeManagementConfig") - Tags: Optional[Tags] = prop("Tags") - PropagateTags: Optional[bool] = prop("PropagateTags") - Timeout: Optional[Timeout] = prop("Timeout") - Tracing: Optional[Tracing] = prop("Tracing") - VersionDescription: Optional[PassThroughProp] = prop("VersionDescription") - VpcConfig: Optional[VpcConfig] = prop("VpcConfig") - LoggingConfig: Optional[PassThroughProp] = passthrough_prop( + SnapStart: SnapStart | None = prop("SnapStart") + RuntimeManagementConfig: RuntimeManagementConfig | None = prop("RuntimeManagementConfig") + Tags: Tags | None = prop("Tags") + PropagateTags: bool | None = prop("PropagateTags") + Timeout: Timeout | None = prop("Timeout") + Tracing: Tracing | None = prop("Tracing") + VersionDescription: PassThroughProp | None = prop("VersionDescription") + VpcConfig: VpcConfig | None = prop("VpcConfig") + LoggingConfig: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "LoggingConfig", ["AWS::Lambda::Function", "Properties", "LoggingConfig"], ) - RecursiveLoop: Optional[PassThroughProp] = passthrough_prop( + RecursiveLoop: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "RecursiveLoop", ["AWS::Lambda::Function", "Properties", "RecursiveLoop"], ) - SourceKMSKeyArn: Optional[PassThroughProp] = passthrough_prop( + SourceKMSKeyArn: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "SourceKMSKeyArn", ["AWS::Lambda::Function.Code", "SourceKMSKeyArn"], ) - CapacityProviderConfig: Optional[CapacityProviderConfig] = prop("CapacityProviderConfig") - FunctionScalingConfig: Optional[PassThroughProp] = passthrough_prop( + CapacityProviderConfig: CapacityProviderConfig | None = prop("CapacityProviderConfig") + FunctionScalingConfig: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "FunctionScalingConfig", ["AWS::Lambda::Function", "Properties", "FunctionScalingConfig"], ) - VersionDeletionPolicy: Optional[SamIntrinsicable[Union[str, bool]]] = prop("VersionDeletionPolicy") - PublishToLatestPublished: Optional[PassThroughProp] = passthrough_prop( + VersionDeletionPolicy: SamIntrinsicable[Union[str, bool]] | None = prop("VersionDeletionPolicy") + PublishToLatestPublished: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "PublishToLatestPublished", ["AWS::Lambda::Function", "Properties", "PublishToLatestPublished"], ) - TenancyConfig: Optional[PassThroughProp] = passthrough_prop( + TenancyConfig: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "TenancyConfig", ["AWS::Lambda::Function", "Properties", "TenancyConfig"], ) - DurableConfig: Optional[PassThroughProp] = passthrough_prop( + DurableConfig: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "DurableConfig", ["AWS::Lambda::Function", "Properties", "DurableConfig"], @@ -757,93 +750,93 @@ class Properties(BaseModel): class Globals(BaseModel): - Handler: Optional[Handler] = passthrough_prop( + Handler: Handler | None = passthrough_prop( PROPERTIES_STEM, "Handler", ["AWS::Lambda::Function", "Properties", "Handler"], ) - Runtime: Optional[Runtime] = passthrough_prop( + Runtime: Runtime | None = passthrough_prop( PROPERTIES_STEM, "Runtime", ["AWS::Lambda::Function", "Properties", "Runtime"], ) - CodeUri: Optional[CodeUriType] = prop("CodeUri") - DeadLetterQueue: Optional[DeadLetterQueueType] = prop("DeadLetterQueue") - Description: Optional[Description] = prop("Description") - MemorySize: Optional[MemorySize] = prop("MemorySize") - Timeout: Optional[Timeout] = prop("Timeout") - VpcConfig: Optional[VpcConfig] = prop("VpcConfig") - Environment: Optional[Environment] = passthrough_prop( + CodeUri: CodeUriType | None = prop("CodeUri") + DeadLetterQueue: DeadLetterQueueType | None = prop("DeadLetterQueue") + Description: Description | None = prop("Description") + MemorySize: MemorySize | None = prop("MemorySize") + Timeout: Timeout | None = prop("Timeout") + VpcConfig: VpcConfig | None = prop("VpcConfig") + Environment: Environment | None = passthrough_prop( PROPERTIES_STEM, "Environment", ["AWS::Lambda::Function", "Properties", "Environment"], ) - Tags: Optional[Tags] = prop("Tags") - PropagateTags: Optional[bool] = prop("PropagateTags") - Tracing: Optional[Tracing] = prop("Tracing") - KmsKeyArn: Optional[KmsKeyArn] = prop("KmsKeyArn") - Layers: Optional[Layers] = prop("Layers") - AutoPublishAlias: Optional[AutoPublishAlias] = prop("AutoPublishAlias") - DeploymentPreference: Optional[DeploymentPreference] = prop("DeploymentPreference") - RolePath: Optional[RolePath] = passthrough_prop( + Tags: Tags | None = prop("Tags") + PropagateTags: bool | None = prop("PropagateTags") + Tracing: Tracing | None = prop("Tracing") + KmsKeyArn: KmsKeyArn | None = prop("KmsKeyArn") + Layers: Layers | None = prop("Layers") + AutoPublishAlias: AutoPublishAlias | None = prop("AutoPublishAlias") + DeploymentPreference: DeploymentPreference | None = prop("DeploymentPreference") + RolePath: RolePath | None = passthrough_prop( PROPERTIES_STEM, "RolePath", ["AWS::IAM::Role", "Properties", "Path"], ) - PermissionsBoundary: Optional[PermissionsBoundary] = passthrough_prop( + PermissionsBoundary: PermissionsBoundary | None = passthrough_prop( PROPERTIES_STEM, "PermissionsBoundary", ["AWS::IAM::Role", "Properties", "PermissionsBoundary"], ) - ReservedConcurrentExecutions: Optional[ReservedConcurrentExecutions] = prop("ReservedConcurrentExecutions") - ProvisionedConcurrencyConfig: Optional[ProvisionedConcurrencyConfig] = prop("ProvisionedConcurrencyConfig") - AssumeRolePolicyDocument: Optional[AssumeRolePolicyDocument] = prop("AssumeRolePolicyDocument") - EventInvokeConfig: Optional[EventInvokeConfig] = prop("EventInvokeConfig") - Architectures: Optional[Architectures] = passthrough_prop( + ReservedConcurrentExecutions: ReservedConcurrentExecutions | None = prop("ReservedConcurrentExecutions") + ProvisionedConcurrencyConfig: ProvisionedConcurrencyConfig | None = prop("ProvisionedConcurrencyConfig") + AssumeRolePolicyDocument: AssumeRolePolicyDocument | None = prop("AssumeRolePolicyDocument") + EventInvokeConfig: EventInvokeConfig | None = prop("EventInvokeConfig") + Architectures: Architectures | None = passthrough_prop( PROPERTIES_STEM, "Architectures", ["AWS::Lambda::Function", "Properties", "Architectures"], ) - EphemeralStorage: Optional[EphemeralStorage] = passthrough_prop( + EphemeralStorage: EphemeralStorage | None = passthrough_prop( PROPERTIES_STEM, "EphemeralStorage", ["AWS::Lambda::Function", "Properties", "EphemeralStorage"], ) - SnapStart: Optional[SnapStart] = prop("SnapStart") - RuntimeManagementConfig: Optional[RuntimeManagementConfig] = prop("RuntimeManagementConfig") - LoggingConfig: Optional[PassThroughProp] = passthrough_prop( + SnapStart: SnapStart | None = prop("SnapStart") + RuntimeManagementConfig: RuntimeManagementConfig | None = prop("RuntimeManagementConfig") + LoggingConfig: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "LoggingConfig", ["AWS::Lambda::Function", "Properties", "LoggingConfig"], ) - RecursiveLoop: Optional[PassThroughProp] = passthrough_prop( + RecursiveLoop: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "RecursiveLoop", ["AWS::Lambda::Function", "Properties", "RecursiveLoop"], ) - SourceKMSKeyArn: Optional[PassThroughProp] = passthrough_prop( + SourceKMSKeyArn: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "SourceKMSKeyArn", ["AWS::Lambda::Function.Code", "SourceKMSKeyArn"], ) - CapacityProviderConfig: Optional[CapacityProviderConfig] = prop("CapacityProviderConfig") - FunctionScalingConfig: Optional[PassThroughProp] = passthrough_prop( + CapacityProviderConfig: CapacityProviderConfig | None = prop("CapacityProviderConfig") + FunctionScalingConfig: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "FunctionScalingConfig", ["AWS::Lambda::Function", "Properties", "FunctionScalingConfig"], ) - VersionDeletionPolicy: Optional[SamIntrinsicable[Union[str, bool]]] = prop("VersionDeletionPolicy") - PublishToLatestPublished: Optional[PassThroughProp] = passthrough_prop( + VersionDeletionPolicy: SamIntrinsicable[Union[str, bool]] | None = prop("VersionDeletionPolicy") + PublishToLatestPublished: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "PublishToLatestPublished", ["AWS::Lambda::Function", "Properties", "PublishToLatestPublished"], ) - TenancyConfig: Optional[PassThroughProp] = passthrough_prop( + TenancyConfig: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "TenancyConfig", ["AWS::Lambda::Function", "Properties", "TenancyConfig"], ) - DurableConfig: Optional[PassThroughProp] = passthrough_prop( + DurableConfig: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "DurableConfig", ["AWS::Lambda::Function", "Properties", "DurableConfig"], @@ -852,5 +845,5 @@ class Globals(BaseModel): class Resource(ResourceAttributes): Type: Literal["AWS::Serverless::Function"] - Properties: Optional[Properties] - Connectors: Optional[Dict[str, EmbeddedConnector]] + Properties: Properties | None + Connectors: dict[str, EmbeddedConnector] | None diff --git a/samtranslator/internal/schema_source/aws_serverless_graphqlapi.py b/samtranslator/internal/schema_source/aws_serverless_graphqlapi.py index e5a61ea87a..8f5c8e02b1 100644 --- a/samtranslator/internal/schema_source/aws_serverless_graphqlapi.py +++ b/samtranslator/internal/schema_source/aws_serverless_graphqlapi.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Dict, List, Literal, Optional, Union +from typing import Literal, Union from samtranslator.internal.schema_source.common import ( BaseModel, @@ -33,50 +33,50 @@ class LambdaAuthorizerConfig(BaseModel): # Maps to AWS::AppSync::GraphQLApi.LambdaAuthorizerConfig - AuthorizerResultTtlInSeconds: Optional[PassThroughProp] + AuthorizerResultTtlInSeconds: PassThroughProp | None AuthorizerUri: PassThroughProp - IdentityValidationExpression: Optional[PassThroughProp] + IdentityValidationExpression: PassThroughProp | None class OpenIDConnectConfig(BaseModel): # Maps to AWS::AppSync::GraphQLApi.OpenIDConnectConfig - AuthTTL: Optional[PassThroughProp] - ClientId: Optional[PassThroughProp] - IatTTL: Optional[PassThroughProp] - Issuer: Optional[PassThroughProp] + AuthTTL: PassThroughProp | None + ClientId: PassThroughProp | None + IatTTL: PassThroughProp | None + Issuer: PassThroughProp | None class UserPoolConfig(BaseModel): # Maps to AWS::AppSync::GraphQLApi.UserPoolConfig - AppIdClientRegex: Optional[PassThroughProp] - AwsRegion: Optional[PassThroughProp] - DefaultAction: Optional[PassThroughProp] + AppIdClientRegex: PassThroughProp | None + AwsRegion: PassThroughProp | None + DefaultAction: PassThroughProp | None UserPoolId: PassThroughProp class Authorizer(BaseModel): Type: AuthenticationTypes = authprovider("Type") # Maps to AWS::AppSync::GraphQLApi.AdditionalAuthenticationProvider - LambdaAuthorizer: Optional[LambdaAuthorizerConfig] - OpenIDConnect: Optional[OpenIDConnectConfig] - UserPool: Optional[UserPoolConfig] + LambdaAuthorizer: LambdaAuthorizerConfig | None + OpenIDConnect: OpenIDConnectConfig | None + UserPool: UserPoolConfig | None class Auth(Authorizer): - Additional: Optional[List[Authorizer]] = auth("Additional") + Additional: list[Authorizer] | None = auth("Additional") class ApiKey(BaseModel): - ApiKeyId: Optional[PassThroughProp] = apikey("ApiKeyId") - Description: Optional[PassThroughProp] = apikey("Description") - ExpiresOn: Optional[PassThroughProp] = apikey("ExpiresOn") + ApiKeyId: PassThroughProp | None = apikey("ApiKeyId") + Description: PassThroughProp | None = apikey("Description") + ExpiresOn: PassThroughProp | None = apikey("ExpiresOn") class Logging(BaseModel): # Maps to AWS::AppSync::GraphQLApi LogConfig - CloudWatchLogsRoleArn: Optional[PassThroughProp] - ExcludeVerboseContent: Optional[PassThroughProp] - FieldLogLevel: Optional[PassThroughProp] + CloudWatchLogsRoleArn: PassThroughProp | None + ExcludeVerboseContent: PassThroughProp | None + FieldLogLevel: PassThroughProp | None class DeltaSync(BaseModel): @@ -88,27 +88,27 @@ class DeltaSync(BaseModel): class DynamoDBDataSource(BaseModel): TableName: PassThroughProp = dynamodbdatasource("TableName") - ServiceRoleArn: Optional[PassThroughProp] = dynamodbdatasource("ServiceRoleArn") - TableArn: Optional[PassThroughProp] = dynamodbdatasource("TableArn") - Permissions: Optional[PermissionsType] = dynamodbdatasource("Permissions") - Name: Optional[PassThroughProp] = dynamodbdatasource("Name") - Description: Optional[PassThroughProp] = dynamodbdatasource("Description") - Region: Optional[PassThroughProp] = dynamodbdatasource("Region") - DeltaSync: Optional[DeltaSync] = dynamodbdatasource("DeltaSync") - UseCallerCredentials: Optional[PassThroughProp] = dynamodbdatasource("UseCallerCredentials") - Versioned: Optional[PassThroughProp] = dynamodbdatasource("Versioned") + ServiceRoleArn: PassThroughProp | None = dynamodbdatasource("ServiceRoleArn") + TableArn: PassThroughProp | None = dynamodbdatasource("TableArn") + Permissions: PermissionsType | None = dynamodbdatasource("Permissions") + Name: PassThroughProp | None = dynamodbdatasource("Name") + Description: PassThroughProp | None = dynamodbdatasource("Description") + Region: PassThroughProp | None = dynamodbdatasource("Region") + DeltaSync: DeltaSync | None = dynamodbdatasource("DeltaSync") + UseCallerCredentials: PassThroughProp | None = dynamodbdatasource("UseCallerCredentials") + Versioned: PassThroughProp | None = dynamodbdatasource("Versioned") class LambdaDataSource(BaseModel): FunctionArn: PassThroughProp = lambdadatasource("FunctionArn") - ServiceRoleArn: Optional[PassThroughProp] = lambdadatasource("ServiceRoleArn") - Name: Optional[PassThroughProp] = lambdadatasource("Name") - Description: Optional[PassThroughProp] = lambdadatasource("Description") + ServiceRoleArn: PassThroughProp | None = lambdadatasource("ServiceRoleArn") + Name: PassThroughProp | None = lambdadatasource("Name") + Description: PassThroughProp | None = lambdadatasource("Description") class DataSources(BaseModel): - DynamoDb: Optional[Dict[str, DynamoDBDataSource]] = datasource("DynamoDb") - Lambda: Optional[Dict[str, LambdaDataSource]] = datasource("Lambda") + DynamoDb: dict[str, DynamoDBDataSource] | None = datasource("DynamoDb") + Lambda: dict[str, LambdaDataSource] | None = datasource("Lambda") class Runtime(BaseModel): @@ -124,46 +124,46 @@ class LambdaConflictHandlerConfig(BaseModel): class Sync(BaseModel): # Maps to AWS::AppSync::FunctionConfiguration.SyncConfig ConflictDetection: PassThroughProp - ConflictHandler: Optional[PassThroughProp] - LambdaConflictHandlerConfig: Optional[LambdaConflictHandlerConfig] + ConflictHandler: PassThroughProp | None + LambdaConflictHandlerConfig: LambdaConflictHandlerConfig | None class Function(BaseModel): - DataSource: Optional[SamIntrinsicable[str]] = function("DataSource") - Runtime: Optional[Runtime] = function("Runtime") - InlineCode: Optional[PassThroughProp] = function("InlineCode") - CodeUri: Optional[PassThroughProp] = function("CodeUri") - Description: Optional[PassThroughProp] = function("Description") - MaxBatchSize: Optional[PassThroughProp] = function("MaxBatchSize") - Name: Optional[str] = function("Name") - Id: Optional[PassThroughProp] = function("Id") - Sync: Optional[Sync] = function("Sync") + DataSource: SamIntrinsicable[str] | None = function("DataSource") + Runtime: Runtime | None = function("Runtime") + InlineCode: PassThroughProp | None = function("InlineCode") + CodeUri: PassThroughProp | None = function("CodeUri") + Description: PassThroughProp | None = function("Description") + MaxBatchSize: PassThroughProp | None = function("MaxBatchSize") + Name: str | None = function("Name") + Id: PassThroughProp | None = function("Id") + Sync: Sync | None = function("Sync") class Caching(BaseModel): # Maps to AWS::AppSync::Resolver.CachingConfig Ttl: PassThroughProp - CachingKeys: Optional[List[PassThroughProp]] + CachingKeys: list[PassThroughProp] | None class Resolver(BaseModel): - FieldName: Optional[str] = resolver("FieldName") - Caching: Optional[Caching] = resolver("Caching") - InlineCode: Optional[PassThroughProp] = resolver("InlineCode") - CodeUri: Optional[PassThroughProp] = resolver("CodeUri") - MaxBatchSize: Optional[PassThroughProp] = resolver("MaxBatchSize") - Pipeline: Optional[List[str]] = resolver( + FieldName: str | None = resolver("FieldName") + Caching: Caching | None = resolver("Caching") + InlineCode: PassThroughProp | None = resolver("InlineCode") + CodeUri: PassThroughProp | None = resolver("CodeUri") + MaxBatchSize: PassThroughProp | None = resolver("MaxBatchSize") + Pipeline: list[str] | None = resolver( "Pipeline" ) # keeping it optional allows for easier validation in to_cloudformation with better error messages - Runtime: Optional[Runtime] = resolver("Runtime") - Sync: Optional[Sync] = resolver("Sync") + Runtime: Runtime | None = resolver("Runtime") + Sync: Sync | None = resolver("Sync") class DomainName(BaseModel): # Maps to AWS::AppSync::DomainName CertificateArn: PassThroughProp DomainName: PassThroughProp - Description: Optional[PassThroughProp] + Description: PassThroughProp | None class Cache(BaseModel): @@ -171,29 +171,29 @@ class Cache(BaseModel): ApiCachingBehavior: PassThroughProp Ttl: PassThroughProp Type: PassThroughProp - AtRestEncryptionEnabled: Optional[PassThroughProp] - TransitEncryptionEnabled: Optional[PassThroughProp] + AtRestEncryptionEnabled: PassThroughProp | None + TransitEncryptionEnabled: PassThroughProp | None class Properties(BaseModel): Auth: Auth = properties("Auth") - Tags: Optional[DictStrAny] = properties("Tags") - Name: Optional[PassThroughProp] = properties("Name") - XrayEnabled: Optional[bool] = properties("XrayEnabled") - SchemaInline: Optional[PassThroughProp] = properties("SchemaInline") - SchemaUri: Optional[PassThroughProp] = properties("SchemaUri") - Logging: Optional[Union[Logging, bool]] = properties("Logging") - DataSources: Optional[DataSources] = properties("DataSources") - Functions: Optional[Dict[str, Function]] = properties("Functions") - Resolvers: Optional[Dict[str, Dict[str, Resolver]]] = properties("Resolvers") - ApiKeys: Optional[Dict[str, ApiKey]] = properties("ApiKeys") - DomainName: Optional[DomainName] = properties("DomainName") - Cache: Optional[Cache] = properties("Cache") - Visibility: Optional[PassThroughProp] # TODO: add documentation when available in sam-docs.json - OwnerContact: Optional[PassThroughProp] # TODO: add documentation when available in sam-docs.json - IntrospectionConfig: Optional[PassThroughProp] # TODO: add documentation when available in sam-docs.json - QueryDepthLimit: Optional[PassThroughProp] # TODO: add documentation when available in sam-docs.json - ResolverCountLimit: Optional[PassThroughProp] # TODO: add documentation when available in sam-docs.json + Tags: DictStrAny | None = properties("Tags") + Name: PassThroughProp | None = properties("Name") + XrayEnabled: bool | None = properties("XrayEnabled") + SchemaInline: PassThroughProp | None = properties("SchemaInline") + SchemaUri: PassThroughProp | None = properties("SchemaUri") + Logging: Union[Logging, bool] | None = properties("Logging") + DataSources: DataSources | None = properties("DataSources") + Functions: dict[str, Function] | None = properties("Functions") + Resolvers: dict[str, dict[str, Resolver]] | None = properties("Resolvers") + ApiKeys: dict[str, ApiKey] | None = properties("ApiKeys") + DomainName: DomainName | None = properties("DomainName") + Cache: Cache | None = properties("Cache") + Visibility: PassThroughProp | None # TODO: add documentation when available in sam-docs.json + OwnerContact: PassThroughProp | None # TODO: add documentation when available in sam-docs.json + IntrospectionConfig: PassThroughProp | None # TODO: add documentation when available in sam-docs.json + QueryDepthLimit: PassThroughProp | None # TODO: add documentation when available in sam-docs.json + ResolverCountLimit: PassThroughProp | None # TODO: add documentation when available in sam-docs.json class Resource(BaseModel): diff --git a/samtranslator/internal/schema_source/aws_serverless_httpapi.py b/samtranslator/internal/schema_source/aws_serverless_httpapi.py index 5067e458a9..7a659583cb 100644 --- a/samtranslator/internal/schema_source/aws_serverless_httpapi.py +++ b/samtranslator/internal/schema_source/aws_serverless_httpapi.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Dict, List, Literal, Optional, Union +from typing import Literal, Union from samtranslator.internal.schema_source.aws_serverless_connector import EmbeddedConnector from samtranslator.internal.schema_source.common import ( @@ -25,17 +25,17 @@ class OAuth2Authorizer(BaseModel): - AuthorizationScopes: Optional[List[str]] = oauth2authorizer("AuthorizationScopes") - IdentitySource: Optional[str] = oauth2authorizer("IdentitySource") - JwtConfiguration: Optional[PassThroughProp] = oauth2authorizer("JwtConfiguration") + AuthorizationScopes: list[str] | None = oauth2authorizer("AuthorizationScopes") + IdentitySource: str | None = oauth2authorizer("IdentitySource") + JwtConfiguration: PassThroughProp | None = oauth2authorizer("JwtConfiguration") class LambdaAuthorizerIdentity(BaseModel): - Context: Optional[List[str]] = lambdauthorizeridentity("Context") - Headers: Optional[List[str]] = lambdauthorizeridentity("Headers") - QueryStrings: Optional[List[str]] = lambdauthorizeridentity("QueryStrings") - ReauthorizeEvery: Optional[int] = lambdauthorizeridentity("ReauthorizeEvery") - StageVariables: Optional[List[str]] = lambdauthorizeridentity("StageVariables") + Context: list[str] | None = lambdauthorizeridentity("Context") + Headers: list[str] | None = lambdauthorizeridentity("Headers") + QueryStrings: list[str] | None = lambdauthorizeridentity("QueryStrings") + ReauthorizeEvery: int | None = lambdauthorizeridentity("ReauthorizeEvery") + StageVariables: list[str] | None = lambdauthorizeridentity("StageVariables") class LambdaAuthorizer(BaseModel): @@ -43,55 +43,47 @@ class LambdaAuthorizer(BaseModel): AuthorizerPayloadFormatVersion: Union[Literal["1.0", "2.0"], float] = lambdaauthorizer( "AuthorizerPayloadFormatVersion" ) - EnableSimpleResponses: Optional[bool] = lambdaauthorizer("EnableSimpleResponses") + EnableSimpleResponses: bool | None = lambdaauthorizer("EnableSimpleResponses") FunctionArn: SamIntrinsicable[str] = lambdaauthorizer("FunctionArn") - FunctionInvokeRole: Optional[SamIntrinsicable[str]] = lambdaauthorizer("FunctionInvokeRole") - EnableFunctionDefaultPermissions: Optional[bool] = lambdaauthorizer("EnableFunctionDefaultPermissions") - Identity: Optional[LambdaAuthorizerIdentity] = lambdaauthorizer("Identity") + FunctionInvokeRole: SamIntrinsicable[str] | None = lambdaauthorizer("FunctionInvokeRole") + EnableFunctionDefaultPermissions: bool | None = lambdaauthorizer("EnableFunctionDefaultPermissions") + Identity: LambdaAuthorizerIdentity | None = lambdaauthorizer("Identity") class Auth(BaseModel): # TODO: Docs doesn't say it's a map - Authorizers: Optional[ - Dict[ - str, - Union[ - OAuth2Authorizer, - LambdaAuthorizer, - ], - ] - ] = auth("Authorizers") - DefaultAuthorizer: Optional[str] = auth("DefaultAuthorizer") - EnableIamAuthorizer: Optional[bool] = auth("EnableIamAuthorizer") + Authorizers: dict[str, Union[OAuth2Authorizer, LambdaAuthorizer]] | None = auth("Authorizers") + DefaultAuthorizer: str | None = auth("DefaultAuthorizer") + EnableIamAuthorizer: bool | None = auth("EnableIamAuthorizer") class CorsConfiguration(BaseModel): - AllowCredentials: Optional[bool] = corsconfiguration("AllowCredentials") - AllowHeaders: Optional[List[str]] = corsconfiguration("AllowHeaders") - AllowMethods: Optional[List[str]] = corsconfiguration("AllowMethods") - AllowOrigins: Optional[List[str]] = corsconfiguration("AllowOrigins") - ExposeHeaders: Optional[List[str]] = corsconfiguration("ExposeHeaders") - MaxAge: Optional[int] = corsconfiguration("MaxAge") + AllowCredentials: bool | None = corsconfiguration("AllowCredentials") + AllowHeaders: list[str] | None = corsconfiguration("AllowHeaders") + AllowMethods: list[str] | None = corsconfiguration("AllowMethods") + AllowOrigins: list[str] | None = corsconfiguration("AllowOrigins") + ExposeHeaders: list[str] | None = corsconfiguration("ExposeHeaders") + MaxAge: int | None = corsconfiguration("MaxAge") class DefinitionUri(BaseModel): Bucket: str = definitionuri("Bucket") Key: str = definitionuri("Key") - Version: Optional[str] = definitionuri("Version") + Version: str | None = definitionuri("Version") class Route53(BaseModel): - DistributionDomainName: Optional[PassThroughProp] = route53("DistributionDomainName") - EvaluateTargetHealth: Optional[PassThroughProp] = route53("EvaluateTargetHealth") - HostedZoneId: Optional[PassThroughProp] = route53("HostedZoneId") - HostedZoneName: Optional[PassThroughProp] = route53("HostedZoneName") - IpV6: Optional[bool] = route53("IpV6") - SetIdentifier: Optional[PassThroughProp] = passthrough_prop( + DistributionDomainName: PassThroughProp | None = route53("DistributionDomainName") + EvaluateTargetHealth: PassThroughProp | None = route53("EvaluateTargetHealth") + HostedZoneId: PassThroughProp | None = route53("HostedZoneId") + HostedZoneName: PassThroughProp | None = route53("HostedZoneName") + IpV6: bool | None = route53("IpV6") + SetIdentifier: PassThroughProp | None = passthrough_prop( "sam-property-httpapi-route53configuration", "SetIdentifier", ["AWS::Route53::RecordSetGroup.RecordSet", "SetIdentifier"], ) - Region: Optional[PassThroughProp] = passthrough_prop( + Region: PassThroughProp | None = passthrough_prop( "sam-property-httpapi-route53configuration", "Region", ["AWS::Route53::RecordSetGroup.RecordSet", "Region"], @@ -99,59 +91,59 @@ class Route53(BaseModel): class Domain(BaseModel): - BasePath: Optional[List[str]] = domain("BasePath") + BasePath: list[str] | None = domain("BasePath") CertificateArn: PassThroughProp = domain("CertificateArn") DomainName: PassThroughProp = domain("DomainName") - EndpointConfiguration: Optional[SamIntrinsicable[Literal["REGIONAL"]]] = domain("EndpointConfiguration") - MutualTlsAuthentication: Optional[PassThroughProp] = domain("MutualTlsAuthentication") - OwnershipVerificationCertificateArn: Optional[PassThroughProp] = domain("OwnershipVerificationCertificateArn") - Route53: Optional[Route53] = domain("Route53") - SecurityPolicy: Optional[PassThroughProp] = domain("SecurityPolicy") + EndpointConfiguration: SamIntrinsicable[Literal["REGIONAL"]] | None = domain("EndpointConfiguration") + MutualTlsAuthentication: PassThroughProp | None = domain("MutualTlsAuthentication") + OwnershipVerificationCertificateArn: PassThroughProp | None = domain("OwnershipVerificationCertificateArn") + Route53: Route53 | None = domain("Route53") + SecurityPolicy: PassThroughProp | None = domain("SecurityPolicy") -AccessLogSettings = Optional[PassThroughProp] -StageVariables = Optional[PassThroughProp] -Tags = Optional[DictStrAny] -RouteSettings = Optional[PassThroughProp] -FailOnWarnings = Optional[PassThroughProp] -CorsConfigurationType = Optional[PassThroughProp] -DefaultRouteSettings = Optional[PassThroughProp] +AccessLogSettings = PassThroughProp | None +StageVariables = PassThroughProp | None +Tags = DictStrAny | None +RouteSettings = PassThroughProp | None +FailOnWarnings = PassThroughProp | None +CorsConfigurationType = PassThroughProp | None +DefaultRouteSettings = PassThroughProp | None class Properties(BaseModel): - AccessLogSettings: Optional[AccessLogSettings] = properties("AccessLogSettings") - Auth: Optional[Auth] = properties("Auth") + AccessLogSettings: AccessLogSettings | None = properties("AccessLogSettings") + Auth: Auth | None = properties("Auth") # TODO: Also string like in the docs? - CorsConfiguration: Optional[CorsConfigurationType] = properties("CorsConfiguration") - DefaultRouteSettings: Optional[DefaultRouteSettings] = properties("DefaultRouteSettings") - DefinitionBody: Optional[DictStrAny] = properties("DefinitionBody") - DefinitionUri: Optional[Union[str, DefinitionUri]] = properties("DefinitionUri") - Description: Optional[str] = properties("Description") - DisableExecuteApiEndpoint: Optional[PassThroughProp] = properties("DisableExecuteApiEndpoint") - Domain: Optional[Domain] = properties("Domain") - FailOnWarnings: Optional[FailOnWarnings] = properties("FailOnWarnings") - RouteSettings: Optional[RouteSettings] = properties("RouteSettings") - StageName: Optional[PassThroughProp] = properties("StageName") - StageVariables: Optional[StageVariables] = properties("StageVariables") - Tags: Optional[Tags] = properties("Tags") - PropagateTags: Optional[bool] = properties("PropagateTags") - Name: Optional[PassThroughProp] = properties("Name") + CorsConfiguration: CorsConfigurationType | None = properties("CorsConfiguration") + DefaultRouteSettings: DefaultRouteSettings | None = properties("DefaultRouteSettings") + DefinitionBody: DictStrAny | None = properties("DefinitionBody") + DefinitionUri: Union[str, DefinitionUri] | None = properties("DefinitionUri") + Description: str | None = properties("Description") + DisableExecuteApiEndpoint: PassThroughProp | None = properties("DisableExecuteApiEndpoint") + Domain: Domain | None = properties("Domain") + FailOnWarnings: FailOnWarnings | None = properties("FailOnWarnings") + RouteSettings: RouteSettings | None = properties("RouteSettings") + StageName: PassThroughProp | None = properties("StageName") + StageVariables: StageVariables | None = properties("StageVariables") + Tags: Tags | None = properties("Tags") + PropagateTags: bool | None = properties("PropagateTags") + Name: PassThroughProp | None = properties("Name") class Globals(BaseModel): - Auth: Optional[Auth] = properties("Auth") - AccessLogSettings: Optional[AccessLogSettings] = properties("AccessLogSettings") - StageVariables: Optional[StageVariables] = properties("StageVariables") - Tags: Optional[Tags] = properties("Tags") - RouteSettings: Optional[RouteSettings] = properties("RouteSettings") - FailOnWarnings: Optional[FailOnWarnings] = properties("FailOnWarnings") - Domain: Optional[Domain] = properties("Domain") - CorsConfiguration: Optional[CorsConfigurationType] = properties("CorsConfiguration") - DefaultRouteSettings: Optional[DefaultRouteSettings] = properties("DefaultRouteSettings") - PropagateTags: Optional[bool] = properties("PropagateTags") + Auth: Auth | None = properties("Auth") + AccessLogSettings: AccessLogSettings | None = properties("AccessLogSettings") + StageVariables: StageVariables | None = properties("StageVariables") + Tags: Tags | None = properties("Tags") + RouteSettings: RouteSettings | None = properties("RouteSettings") + FailOnWarnings: FailOnWarnings | None = properties("FailOnWarnings") + Domain: Domain | None = properties("Domain") + CorsConfiguration: CorsConfigurationType | None = properties("CorsConfiguration") + DefaultRouteSettings: DefaultRouteSettings | None = properties("DefaultRouteSettings") + PropagateTags: bool | None = properties("PropagateTags") class Resource(ResourceAttributes): Type: Literal["AWS::Serverless::HttpApi"] - Properties: Optional[Properties] - Connectors: Optional[Dict[str, EmbeddedConnector]] + Properties: Properties | None + Connectors: dict[str, EmbeddedConnector] | None diff --git a/samtranslator/internal/schema_source/aws_serverless_layerversion.py b/samtranslator/internal/schema_source/aws_serverless_layerversion.py index c4f038b942..fefa8d0ca8 100644 --- a/samtranslator/internal/schema_source/aws_serverless_layerversion.py +++ b/samtranslator/internal/schema_source/aws_serverless_layerversion.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Literal, Optional, Union +from typing import Literal, Union from samtranslator.internal.schema_source.common import ( BaseModel, @@ -29,7 +29,7 @@ class ContentUri(BaseModel): "Key", ["AWS::Lambda::LayerVersion.Content", "S3Key"], ) - Version: Optional[PassThroughProp] = passthrough_prop( + Version: PassThroughProp | None = passthrough_prop( CONTENT_URI_STEM, "Version", ["AWS::Lambda::LayerVersion.Content", "S3ObjectVersion"], @@ -37,30 +37,30 @@ class ContentUri(BaseModel): class Properties(BaseModel): - CompatibleArchitectures: Optional[PassThroughProp] = passthrough_prop( + CompatibleArchitectures: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "CompatibleArchitectures", ["AWS::Lambda::LayerVersion", "Properties", "CompatibleArchitectures"], ) - CompatibleRuntimes: Optional[PassThroughProp] = passthrough_prop( + CompatibleRuntimes: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "CompatibleRuntimes", ["AWS::Lambda::LayerVersion", "Properties", "CompatibleRuntimes"], ) - PublishLambdaVersion: Optional[bool] = properties("PublishLambdaVersion") + PublishLambdaVersion: bool | None = properties("PublishLambdaVersion") ContentUri: Union[str, ContentUri] = properties("ContentUri") - Description: Optional[PassThroughProp] = passthrough_prop( + Description: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "Description", ["AWS::Lambda::LayerVersion", "Properties", "Description"], ) - LayerName: Optional[PassThroughProp] = properties("LayerName") - LicenseInfo: Optional[PassThroughProp] = passthrough_prop( + LayerName: PassThroughProp | None = properties("LayerName") + LicenseInfo: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "LicenseInfo", ["AWS::Lambda::LayerVersion", "Properties", "LicenseInfo"], ) - RetentionPolicy: Optional[SamIntrinsicable[str]] = properties("RetentionPolicy") + RetentionPolicy: SamIntrinsicable[str] | None = properties("RetentionPolicy") class Resource(ResourceAttributes): @@ -69,4 +69,4 @@ class Resource(ResourceAttributes): class Globals(BaseModel): - PublishLambdaVersion: Optional[bool] = properties("PublishLambdaVersion") + PublishLambdaVersion: bool | None = properties("PublishLambdaVersion") diff --git a/samtranslator/internal/schema_source/aws_serverless_simpletable.py b/samtranslator/internal/schema_source/aws_serverless_simpletable.py index 016c67864b..65163005a3 100644 --- a/samtranslator/internal/schema_source/aws_serverless_simpletable.py +++ b/samtranslator/internal/schema_source/aws_serverless_simpletable.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, Literal, Optional +from typing import Any, Literal from samtranslator.internal.schema_source.aws_serverless_connector import EmbeddedConnector from samtranslator.internal.schema_source.common import ( @@ -31,36 +31,36 @@ class PrimaryKey(BaseModel): ) -SSESpecification = Optional[PassThroughProp] +SSESpecification = PassThroughProp | None class Properties(BaseModel): - PointInTimeRecoverySpecification: Optional[PassThroughProp] = passthrough_prop( + PointInTimeRecoverySpecification: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "ProvisionedThroughput", ["AWS::DynamoDB::Table", "Properties", "PointInTimeRecoverySpecification"], ) - PrimaryKey: Optional[PrimaryKey] = properties("PrimaryKey") - ProvisionedThroughput: Optional[PassThroughProp] = passthrough_prop( + PrimaryKey: PrimaryKey | None = properties("PrimaryKey") + ProvisionedThroughput: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "ProvisionedThroughput", ["AWS::DynamoDB::Table", "Properties", "ProvisionedThroughput"], ) - SSESpecification: Optional[SSESpecification] = passthrough_prop( + SSESpecification: SSESpecification | None = passthrough_prop( PROPERTIES_STEM, "SSESpecification", ["AWS::DynamoDB::Table", "Properties", "SSESpecification"], ) - TableName: Optional[PassThroughProp] = passthrough_prop( + TableName: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "TableName", ["AWS::DynamoDB::Table", "Properties", "TableName"], ) - Tags: Optional[Dict[str, Any]] = properties("Tags") + Tags: dict[str, Any] | None = properties("Tags") class Globals(BaseModel): - SSESpecification: Optional[SSESpecification] = passthrough_prop( + SSESpecification: SSESpecification | None = passthrough_prop( PROPERTIES_STEM, "SSESpecification", ["AWS::DynamoDB::Table", "Properties", "SSESpecification"], @@ -69,5 +69,5 @@ class Globals(BaseModel): class Resource(ResourceAttributes): Type: Literal["AWS::Serverless::SimpleTable"] - Properties: Optional[Properties] - Connectors: Optional[Dict[str, EmbeddedConnector]] + Properties: Properties | None + Connectors: dict[str, EmbeddedConnector] | None diff --git a/samtranslator/internal/schema_source/aws_serverless_statemachine.py b/samtranslator/internal/schema_source/aws_serverless_statemachine.py index 138bd34276..f2b73f857f 100644 --- a/samtranslator/internal/schema_source/aws_serverless_statemachine.py +++ b/samtranslator/internal/schema_source/aws_serverless_statemachine.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Dict, List, Literal, Optional, Union +from typing import Literal, Union from samtranslator.internal.schema_source.aws_serverless_connector import EmbeddedConnector from samtranslator.internal.schema_source.common import ( @@ -29,9 +29,9 @@ class DeadLetterConfig(BaseModel): - Arn: Optional[PassThroughProp] = deadletterconfig("Arn") - QueueLogicalId: Optional[str] = deadletterconfig("QueueLogicalId") - Type: Optional[Literal["SQS"]] = deadletterconfig("Type") + Arn: PassThroughProp | None = deadletterconfig("Arn") + QueueLogicalId: str | None = deadletterconfig("QueueLogicalId") + Type: Literal["SQS"] | None = deadletterconfig("Type") class ScheduleTarget(BaseModel): @@ -39,16 +39,16 @@ class ScheduleTarget(BaseModel): class ScheduleEventProperties(BaseModel): - DeadLetterConfig: Optional[DeadLetterConfig] = scheduleeventproperties("DeadLetterConfig") - Description: Optional[PassThroughProp] = scheduleeventproperties("Description") - Enabled: Optional[bool] = scheduleeventproperties("Enabled") - Input: Optional[PassThroughProp] = scheduleeventproperties("Input") - Name: Optional[PassThroughProp] = scheduleeventproperties("Name") - RetryPolicy: Optional[PassThroughProp] = scheduleeventproperties("RetryPolicy") - Schedule: Optional[PassThroughProp] = scheduleeventproperties("Schedule") - State: Optional[PassThroughProp] = scheduleeventproperties("State") - Target: Optional[ScheduleTarget] = scheduleeventproperties("Target") - RoleArn: Optional[PassThroughProp] = passthrough_prop( + DeadLetterConfig: DeadLetterConfig | None = scheduleeventproperties("DeadLetterConfig") + Description: PassThroughProp | None = scheduleeventproperties("Description") + Enabled: bool | None = scheduleeventproperties("Enabled") + Input: PassThroughProp | None = scheduleeventproperties("Input") + Name: PassThroughProp | None = scheduleeventproperties("Name") + RetryPolicy: PassThroughProp | None = scheduleeventproperties("RetryPolicy") + Schedule: PassThroughProp | None = scheduleeventproperties("Schedule") + State: PassThroughProp | None = scheduleeventproperties("State") + Target: ScheduleTarget | None = scheduleeventproperties("Target") + RoleArn: PassThroughProp | None = passthrough_prop( "sam-property-statemachine-statemachineschedule", "RoleArn", ["AWS::Scheduler::Schedule.Target", "RoleArn"], @@ -61,22 +61,22 @@ class ScheduleEvent(BaseModel): class ScheduleV2EventProperties(BaseModel): - DeadLetterConfig: Optional[DeadLetterConfig] = scheduleeventv2properties("DeadLetterConfig") - Description: Optional[PassThroughProp] = scheduleeventv2properties("Description") - EndDate: Optional[PassThroughProp] = scheduleeventv2properties("EndDate") - FlexibleTimeWindow: Optional[PassThroughProp] = scheduleeventv2properties("FlexibleTimeWindow") - GroupName: Optional[PassThroughProp] = scheduleeventv2properties("GroupName") - Input: Optional[PassThroughProp] = scheduleeventv2properties("Input") - KmsKeyArn: Optional[PassThroughProp] = scheduleeventv2properties("KmsKeyArn") - Name: Optional[PassThroughProp] = scheduleeventv2properties("Name") - PermissionsBoundary: Optional[PassThroughProp] = scheduleeventv2properties("PermissionsBoundary") - RetryPolicy: Optional[PassThroughProp] = scheduleeventv2properties("RetryPolicy") - RoleArn: Optional[PassThroughProp] = scheduleeventv2properties("RoleArn") - ScheduleExpression: Optional[PassThroughProp] = scheduleeventv2properties("ScheduleExpression") - ScheduleExpressionTimezone: Optional[PassThroughProp] = scheduleeventv2properties("ScheduleExpressionTimezone") - StartDate: Optional[PassThroughProp] = scheduleeventv2properties("StartDate") - State: Optional[PassThroughProp] = scheduleeventv2properties("State") - OmitName: Optional[bool] = scheduleeventv2properties("OmitName") + DeadLetterConfig: DeadLetterConfig | None = scheduleeventv2properties("DeadLetterConfig") + Description: PassThroughProp | None = scheduleeventv2properties("Description") + EndDate: PassThroughProp | None = scheduleeventv2properties("EndDate") + FlexibleTimeWindow: PassThroughProp | None = scheduleeventv2properties("FlexibleTimeWindow") + GroupName: PassThroughProp | None = scheduleeventv2properties("GroupName") + Input: PassThroughProp | None = scheduleeventv2properties("Input") + KmsKeyArn: PassThroughProp | None = scheduleeventv2properties("KmsKeyArn") + Name: PassThroughProp | None = scheduleeventv2properties("Name") + PermissionsBoundary: PassThroughProp | None = scheduleeventv2properties("PermissionsBoundary") + RetryPolicy: PassThroughProp | None = scheduleeventv2properties("RetryPolicy") + RoleArn: PassThroughProp | None = scheduleeventv2properties("RoleArn") + ScheduleExpression: PassThroughProp | None = scheduleeventv2properties("ScheduleExpression") + ScheduleExpressionTimezone: PassThroughProp | None = scheduleeventv2properties("ScheduleExpressionTimezone") + StartDate: PassThroughProp | None = scheduleeventv2properties("StartDate") + State: PassThroughProp | None = scheduleeventv2properties("State") + OmitName: bool | None = scheduleeventv2properties("OmitName") class ScheduleV2Event(BaseModel): @@ -85,24 +85,24 @@ class ScheduleV2Event(BaseModel): class ResourcePolicy(BaseModel): - AwsAccountBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("AwsAccountBlacklist") - AwsAccountWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("AwsAccountWhitelist") - CustomStatements: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("CustomStatements") - IntrinsicVpcBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpcBlacklist") - IntrinsicVpcWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpcWhitelist") - IntrinsicVpceBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpceBlacklist") - IntrinsicVpceWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IntrinsicVpceWhitelist") - IpRangeBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IpRangeBlacklist") - IpRangeWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("IpRangeWhitelist") - SourceVpcBlacklist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("SourceVpcBlacklist") - SourceVpcWhitelist: Optional[List[Union[str, DictStrAny]]] = resourcepolicy("SourceVpcWhitelist") + AwsAccountBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("AwsAccountBlacklist") + AwsAccountWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("AwsAccountWhitelist") + CustomStatements: list[Union[str, DictStrAny]] | None = resourcepolicy("CustomStatements") + IntrinsicVpcBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpcBlacklist") + IntrinsicVpcWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpcWhitelist") + IntrinsicVpceBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpceBlacklist") + IntrinsicVpceWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("IntrinsicVpceWhitelist") + IpRangeBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("IpRangeBlacklist") + IpRangeWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("IpRangeWhitelist") + SourceVpcBlacklist: list[Union[str, DictStrAny]] | None = resourcepolicy("SourceVpcBlacklist") + SourceVpcWhitelist: list[Union[str, DictStrAny]] | None = resourcepolicy("SourceVpcWhitelist") class CloudWatchEventProperties(BaseModel): - EventBusName: Optional[PassThroughProp] = cloudwatcheventproperties("EventBusName") - Input: Optional[PassThroughProp] = cloudwatcheventproperties("Input") - InputPath: Optional[PassThroughProp] = cloudwatcheventproperties("InputPath") - Pattern: Optional[PassThroughProp] = cloudwatcheventproperties("Pattern") + EventBusName: PassThroughProp | None = cloudwatcheventproperties("EventBusName") + Input: PassThroughProp | None = cloudwatcheventproperties("Input") + InputPath: PassThroughProp | None = cloudwatcheventproperties("InputPath") + Pattern: PassThroughProp | None = cloudwatcheventproperties("Pattern") class CloudWatchEvent(BaseModel): @@ -115,15 +115,15 @@ class EventBridgeRuleTarget(BaseModel): class EventBridgeRuleEventProperties(BaseModel): - DeadLetterConfig: Optional[DeadLetterConfig] = eventbridgeruleeventproperties("DeadLetterConfig") - EventBusName: Optional[PassThroughProp] = eventbridgeruleeventproperties("EventBusName") - Input: Optional[PassThroughProp] = eventbridgeruleeventproperties("Input") - InputPath: Optional[PassThroughProp] = eventbridgeruleeventproperties("InputPath") - Pattern: Optional[PassThroughProp] = eventbridgeruleeventproperties("Pattern") - RetryPolicy: Optional[PassThroughProp] = eventbridgeruleeventproperties("RetryPolicy") - Target: Optional[EventBridgeRuleTarget] = eventbridgeruleeventproperties("Target") - RuleName: Optional[PassThroughProp] = eventbridgeruleeventproperties("RuleName") - InputTransformer: Optional[PassThroughProp] = passthrough_prop( + DeadLetterConfig: DeadLetterConfig | None = eventbridgeruleeventproperties("DeadLetterConfig") + EventBusName: PassThroughProp | None = eventbridgeruleeventproperties("EventBusName") + Input: PassThroughProp | None = eventbridgeruleeventproperties("Input") + InputPath: PassThroughProp | None = eventbridgeruleeventproperties("InputPath") + Pattern: PassThroughProp | None = eventbridgeruleeventproperties("Pattern") + RetryPolicy: PassThroughProp | None = eventbridgeruleeventproperties("RetryPolicy") + Target: EventBridgeRuleTarget | None = eventbridgeruleeventproperties("Target") + RuleName: PassThroughProp | None = eventbridgeruleeventproperties("RuleName") + InputTransformer: PassThroughProp | None = passthrough_prop( "sam-property-statemachine-statemachineeventbridgerule", "InputTransformer", ["AWS::Events::Rule.Target", "InputTransformer"], @@ -136,18 +136,18 @@ class EventBridgeRuleEvent(BaseModel): class Auth(BaseModel): - ApiKeyRequired: Optional[bool] = apiauth("ApiKeyRequired") - AuthorizationScopes: Optional[List[str]] = apiauth("AuthorizationScopes") - Authorizer: Optional[str] = apiauth("Authorizer") - ResourcePolicy: Optional[ResourcePolicy] = apiauth("ResourcePolicy") + ApiKeyRequired: bool | None = apiauth("ApiKeyRequired") + AuthorizationScopes: list[str] | None = apiauth("AuthorizationScopes") + Authorizer: str | None = apiauth("Authorizer") + ResourcePolicy: ResourcePolicy | None = apiauth("ResourcePolicy") class ApiEventProperties(BaseModel): - Auth: Optional[Auth] = apieventproperties("Auth") + Auth: Auth | None = apieventproperties("Auth") Method: str = apieventproperties("Method") Path: str = apieventproperties("Path") - RestApiId: Optional[SamIntrinsicable[str]] = apieventproperties("RestApiId") - UnescapeMappingTemplate: Optional[bool] = apieventproperties("UnescapeMappingTemplate") + RestApiId: SamIntrinsicable[str] | None = apieventproperties("RestApiId") + UnescapeMappingTemplate: bool | None = apieventproperties("UnescapeMappingTemplate") class ApiEvent(BaseModel): @@ -156,41 +156,32 @@ class ApiEvent(BaseModel): class Properties(BaseModel): - Definition: Optional[DictStrAny] = properties("Definition") - DefinitionSubstitutions: Optional[DictStrAny] = properties("DefinitionSubstitutions") - DefinitionUri: Optional[Union[str, PassThroughProp]] = properties("DefinitionUri") - Events: Optional[ - Dict[ - str, - Union[ - ScheduleEvent, - ScheduleV2Event, - CloudWatchEvent, - EventBridgeRuleEvent, - ApiEvent, - ], - ] - ] = properties("Events") - Logging: Optional[PassThroughProp] = properties("Logging") - Name: Optional[PassThroughProp] = properties("Name") - PermissionsBoundary: Optional[PassThroughProp] = properties("PermissionsBoundary") - Policies: Optional[Union[str, DictStrAny, List[Union[str, DictStrAny]]]] = properties("Policies") - Role: Optional[PassThroughProp] = properties("Role") - RolePath: Optional[PassThroughProp] = properties("RolePath") - Tags: Optional[DictStrAny] = properties("Tags") - PropagateTags: Optional[bool] = properties("PropagateTags") - Tracing: Optional[PassThroughProp] = properties("Tracing") - Type: Optional[PassThroughProp] = properties("Type") - AutoPublishAlias: Optional[PassThroughProp] - DeploymentPreference: Optional[PassThroughProp] - UseAliasAsEventTarget: Optional[bool] + Definition: DictStrAny | None = properties("Definition") + DefinitionSubstitutions: DictStrAny | None = properties("DefinitionSubstitutions") + DefinitionUri: Union[str, PassThroughProp] | None = properties("DefinitionUri") + Events: dict[str, Union[ScheduleEvent, ScheduleV2Event, CloudWatchEvent, EventBridgeRuleEvent, ApiEvent]] | None = ( + properties("Events") + ) + Logging: PassThroughProp | None = properties("Logging") + Name: PassThroughProp | None = properties("Name") + PermissionsBoundary: PassThroughProp | None = properties("PermissionsBoundary") + Policies: Union[str, DictStrAny, list[Union[str, DictStrAny]]] | None = properties("Policies") + Role: PassThroughProp | None = properties("Role") + RolePath: PassThroughProp | None = properties("RolePath") + Tags: DictStrAny | None = properties("Tags") + PropagateTags: bool | None = properties("PropagateTags") + Tracing: PassThroughProp | None = properties("Tracing") + Type: PassThroughProp | None = properties("Type") + AutoPublishAlias: PassThroughProp | None + DeploymentPreference: PassThroughProp | None + UseAliasAsEventTarget: bool | None class Resource(ResourceAttributes): Type: Literal["AWS::Serverless::StateMachine"] Properties: Properties - Connectors: Optional[Dict[str, EmbeddedConnector]] + Connectors: dict[str, EmbeddedConnector] | None class Globals(BaseModel): - PropagateTags: Optional[bool] = properties("PropagateTags") + PropagateTags: bool | None = properties("PropagateTags") diff --git a/samtranslator/internal/schema_source/common.py b/samtranslator/internal/schema_source/common.py index 617f8f414b..ca9f9bee04 100644 --- a/samtranslator/internal/schema_source/common.py +++ b/samtranslator/internal/schema_source/common.py @@ -1,7 +1,7 @@ import json from functools import partial from pathlib import Path -from typing import Any, Dict, List, Literal, Optional, TypeVar, Union +from typing import Any, Literal, TypeVar, Union from samtranslator.compat import pydantic from samtranslator.model.types import PassThrough @@ -19,13 +19,13 @@ class PassThroughProp(pydantic.BaseModel): # Intrinsic resolvable by the SAM transform T = TypeVar("T") -SamIntrinsicable = Union[Dict[str, Any], T] -SamIntrinsic = Dict[str, Any] +SamIntrinsicable = Union[dict[str, Any], T] +SamIntrinsic = dict[str, Any] # TODO: Get rid of this in favor of proper types -Unknown = Optional[Any] +Unknown = Any | None -DictStrAny = Dict[str, Any] +DictStrAny = dict[str, Any] LenientBaseModel = pydantic.BaseModel @@ -34,14 +34,14 @@ class PassThroughProp(pydantic.BaseModel): # Connector Permissions -PermissionsType = List[Literal["Read", "Write"]] +PermissionsType = list[Literal["Read", "Write"]] def get_prop(stem: str) -> Any: return partial(_get_prop, stem) -def passthrough_prop(sam_docs_stem: str, sam_docs_name: str, prop_path: List[str]) -> Any: +def passthrough_prop(sam_docs_stem: str, sam_docs_name: str, prop_path: list[str]) -> Any: """ Specifies a pass-through field, where resource_type is the CloudFormation resource type, and path is the list of keys to the property. @@ -92,9 +92,9 @@ class Ref(BaseModel): class ResourceAttributes(BaseModel): - DependsOn: Optional[PassThroughProp] - DeletionPolicy: Optional[PassThroughProp] - Metadata: Optional[PassThroughProp] - UpdateReplacePolicy: Optional[PassThroughProp] - Condition: Optional[PassThroughProp] - IgnoreGlobals: Optional[Union[str, List[str]]] + DependsOn: PassThroughProp | None + DeletionPolicy: PassThroughProp | None + Metadata: PassThroughProp | None + UpdateReplacePolicy: PassThroughProp | None + Condition: PassThroughProp | None + IgnoreGlobals: Union[str, list[str]] | None diff --git a/samtranslator/internal/schema_source/schema.py b/samtranslator/internal/schema_source/schema.py index f83df3db97..2b642504bc 100644 --- a/samtranslator/internal/schema_source/schema.py +++ b/samtranslator/internal/schema_source/schema.py @@ -2,9 +2,10 @@ import argparse import json +from collections.abc import Callable from copy import deepcopy from pathlib import Path -from typing import Any, Callable, Dict, List, Optional, Type, Union +from typing import Any, Union from samtranslator.compat import pydantic from samtranslator.internal.schema_source import ( @@ -24,13 +25,13 @@ class Globals(BaseModel): - Function: Optional[aws_serverless_function.Globals] - Api: Optional[aws_serverless_api.Globals] - HttpApi: Optional[aws_serverless_httpapi.Globals] - SimpleTable: Optional[aws_serverless_simpletable.Globals] - StateMachine: Optional[aws_serverless_statemachine.Globals] - LayerVersion: Optional[aws_serverless_layerversion.Globals] - CapacityProvider: Optional[aws_serverless_capacity_provider.Globals] + Function: aws_serverless_function.Globals | None + Api: aws_serverless_api.Globals | None + HttpApi: aws_serverless_httpapi.Globals | None + SimpleTable: aws_serverless_simpletable.Globals | None + StateMachine: aws_serverless_statemachine.Globals | None + LayerVersion: aws_serverless_layerversion.Globals | None + CapacityProvider: aws_serverless_capacity_provider.Globals | None Resources = Union[ @@ -48,11 +49,11 @@ class Globals(BaseModel): class _ModelWithoutResources(LenientBaseModel): - Globals: Optional[Globals] + Globals: Globals | None class SamModel(_ModelWithoutResources): - Resources: Dict[ + Resources: dict[ str, Union[ Resources, @@ -63,10 +64,10 @@ class SamModel(_ModelWithoutResources): class Model(_ModelWithoutResources): - Resources: Dict[str, Resources] + Resources: dict[str, Resources] -def get_schema(model: Type[pydantic.BaseModel]) -> Dict[str, Any]: +def get_schema(model: type[pydantic.BaseModel]) -> dict[str, Any]: obj = model.schema() # http://json-schema.org/understanding-json-schema/reference/schema.html#schema @@ -86,7 +87,7 @@ def json_dumps(obj: Any) -> str: return json.dumps(obj, indent=2, sort_keys=True) + "\n" -def _replace_in_dict(d: Dict[str, Any], keyword: str, replace: Callable[[Dict[str, Any]], Any]) -> Dict[str, Any]: +def _replace_in_dict(d: dict[str, Any], keyword: str, replace: Callable[[dict[str, Any]], Any]) -> dict[str, Any]: """ Replace any dict containing keyword. @@ -100,7 +101,7 @@ def _replace_in_dict(d: Dict[str, Any], keyword: str, replace: Callable[[Dict[st return d -def _deep_get(d: Dict[str, Any], path: List[str]) -> Dict[str, Any]: +def _deep_get(d: dict[str, Any], path: list[str]) -> dict[str, Any]: """ Returns value at path defined by the keys in `path`. """ @@ -109,7 +110,7 @@ def _deep_get(d: Dict[str, Any], path: List[str]) -> Dict[str, Any]: return d -def _add_embedded_connectors(schema: Dict[str, Any]) -> None: +def _add_embedded_connectors(schema: dict[str, Any]) -> None: """ Add embedded Connectors resource attribute to supported CloudFormation resources. """ @@ -126,7 +127,7 @@ def _add_embedded_connectors(schema: Dict[str, Any]) -> None: schema["definitions"][resource]["properties"]["Connectors"] = embedded_connector -def extend_with_cfn_schema(sam_schema: Dict[str, Any], cfn_schema: Dict[str, Any]) -> None: +def extend_with_cfn_schema(sam_schema: dict[str, Any], cfn_schema: dict[str, Any]) -> None: """ Add CloudFormation resources and template syntax to SAM schema. """ @@ -155,7 +156,7 @@ def extend_with_cfn_schema(sam_schema: Dict[str, Any], cfn_schema: Dict[str, Any _add_embedded_connectors(sam_schema) # Inject CloudFormation documentation to SAM pass-through properties - def replace_passthrough(d: Dict[str, Any]) -> Dict[str, Any]: + def replace_passthrough(d: dict[str, Any]) -> dict[str, Any]: passthrough = d["__samPassThrough"] schema = deepcopy(_deep_get(cfn_schema, passthrough["schemaPath"])) schema["markdownDescription"] = passthrough["markdownDescriptionOverride"] diff --git a/samtranslator/internal/types.py b/samtranslator/internal/types.py index ff5b1e2e17..b71f936308 100644 --- a/samtranslator/internal/types.py +++ b/samtranslator/internal/types.py @@ -1,4 +1,4 @@ -from typing import Callable, Dict +from collections.abc import Callable # Function to retrieve name-to-ARN managed policy map -GetManagedPolicyMap = Callable[[], Dict[str, str]] +GetManagedPolicyMap = Callable[[], dict[str, str]] diff --git a/samtranslator/internal/utils/utils.py b/samtranslator/internal/utils/utils.py index 89d165a6b3..8be14c53c5 100644 --- a/samtranslator/internal/utils/utils.py +++ b/samtranslator/internal/utils/utils.py @@ -1,15 +1,15 @@ -from typing import Any, Dict, Optional, cast +from typing import Any, cast from samtranslator.internal.schema_source.common import PassThroughProp from samtranslator.model.types import PassThrough -def remove_none_values(d: Dict[Any, Any]) -> Dict[Any, Any]: +def remove_none_values(d: dict[Any, Any]) -> dict[Any, Any]: """Returns a copy of the dictionary with no items that have the value None.""" return {k: v for k, v in d.items() if v is not None} -def passthrough_value(v: Optional[PassThroughProp]) -> PassThrough: +def passthrough_value(v: PassThroughProp | None) -> PassThrough: """ Cast PassThroughProp values to PassThrough. diff --git a/samtranslator/intrinsics/actions.py b/samtranslator/intrinsics/actions.py index 3018bf907b..13617be944 100644 --- a/samtranslator/intrinsics/actions.py +++ b/samtranslator/intrinsics/actions.py @@ -1,11 +1,12 @@ import re from abc import ABC -from typing import Any, Callable, Dict, List, Optional, Tuple +from collections.abc import Callable +from typing import Any from samtranslator.model.exceptions import InvalidDocumentException, InvalidTemplateException -def _get_parameter_value(parameters: Dict[str, Any], param_name: str, default: Any = None) -> Any: +def _get_parameter_value(parameters: dict[str, Any], param_name: str, default: Any = None) -> Any: """ Get parameter value from parameters dict, but return default (None) if - it's a CloudFormation internal placeholder. @@ -42,25 +43,23 @@ class Action(ABC): _resource_ref_separator = "." intrinsic_name: str - def resolve_parameter_refs( # noqa: B027 - self, input_dict: Optional[Any], parameters: Dict[str, Any] - ) -> Optional[Any]: + def resolve_parameter_refs(self, input_dict: Any | None, parameters: dict[str, Any]) -> Any | None: # noqa: B027 """ Subclass optionally implement this method to resolve the intrinsic function TODO: input_dict should not be None. """ def resolve_resource_refs( # noqa: B027 - self, input_dict: Optional[Any], supported_resource_refs: Dict[str, Any] - ) -> Optional[Any]: + self, input_dict: Any | None, supported_resource_refs: dict[str, Any] + ) -> Any | None: """ Subclass optionally implement this method to resolve resource references TODO: input_dict should not be None. """ def resolve_resource_id_refs( # noqa: B027 - self, input_dict: Optional[Any], supported_resource_id_refs: Dict[str, Any] - ) -> Optional[Any]: + self, input_dict: Any | None, supported_resource_id_refs: dict[str, Any] + ) -> Any | None: """ Subclass optionally implement this method to resolve resource references TODO: input_dict should not be None. @@ -77,7 +76,7 @@ def can_handle(self, input_dict: Any) -> bool: return isinstance(input_dict, dict) and len(input_dict) == 1 and self.intrinsic_name in input_dict @classmethod - def _parse_resource_reference(cls, ref_value: Any) -> Tuple[Optional[str], Optional[str]]: + def _parse_resource_reference(cls, ref_value: Any) -> tuple[str | None, str | None]: """ Splits a resource reference of structure "LogicalId.Property" and returns the "LogicalId" and "Property" separately. @@ -109,7 +108,7 @@ def _parse_resource_reference(cls, ref_value: Any) -> Tuple[Optional[str], Optio class RefAction(Action): intrinsic_name = "Ref" - def resolve_parameter_refs(self, input_dict: Optional[Any], parameters: Dict[str, Any]) -> Optional[Any]: + def resolve_parameter_refs(self, input_dict: Any | None, parameters: dict[str, Any]) -> Any | None: """ Resolves references that are present in the parameters and returns the value. If it is not in parameters, this method simply returns the input unchanged. @@ -132,9 +131,7 @@ def resolve_parameter_refs(self, input_dict: Optional[Any], parameters: Dict[str # It returns the original input unchanged if the parameter is a CloudFormation internal placeholder return _get_parameter_value(parameters, param_name, input_dict) - def resolve_resource_refs( - self, input_dict: Optional[Any], supported_resource_refs: Dict[str, Any] - ) -> Optional[Any]: + def resolve_resource_refs(self, input_dict: Any | None, supported_resource_refs: dict[str, Any]) -> Any | None: """ Resolves references to some property of a resource. These are runtime properties which can't be converted to a value here. Instead we output another reference that will more actually resolve to the value when @@ -166,8 +163,8 @@ def resolve_resource_refs( return {self.intrinsic_name: resolved_value} def resolve_resource_id_refs( - self, input_dict: Optional[Any], supported_resource_id_refs: Dict[str, Any] - ) -> Optional[Any]: + self, input_dict: Any | None, supported_resource_id_refs: dict[str, Any] + ) -> Any | None: """ Updates references to the old logical id of a resource to the new (generated) logical id. @@ -198,7 +195,7 @@ def resolve_resource_id_refs( class SubAction(Action): intrinsic_name = "Fn::Sub" - def resolve_parameter_refs(self, input_dict: Optional[Any], parameters: Dict[str, Any]) -> Optional[Any]: + def resolve_parameter_refs(self, input_dict: Any | None, parameters: dict[str, Any]) -> Any | None: """ Substitute references found within the string of `Fn::Sub` intrinsic function @@ -224,9 +221,7 @@ def do_replacement(full_ref: str, prop_name: str) -> Any: return self._handle_sub_action(input_dict, do_replacement) - def resolve_resource_refs( - self, input_dict: Optional[Any], supported_resource_refs: Dict[str, Any] - ) -> Optional[Any]: + def resolve_resource_refs(self, input_dict: Any | None, supported_resource_refs: dict[str, Any]) -> Any | None: """ Resolves reference to some property of a resource. Inside string to be substituted, there could be either a "Ref" or a "GetAtt" usage of this property. They have to be handled differently. @@ -286,8 +281,8 @@ def do_replacement(full_ref: str, ref_value: str) -> str: return self._handle_sub_action(input_dict, do_replacement) def resolve_resource_id_refs( - self, input_dict: Optional[Any], supported_resource_id_refs: Dict[str, Any] - ) -> Optional[Any]: + self, input_dict: Any | None, supported_resource_id_refs: dict[str, Any] + ) -> Any | None: """ Resolves reference to some property of a resource. Inside string to be substituted, there could be either a "Ref" or a "GetAtt" usage of this property. They have to be handled differently. @@ -343,9 +338,7 @@ def do_replacement(full_ref: str, ref_value: str) -> str: return self._handle_sub_action(input_dict, do_replacement) - def _handle_sub_action( - self, input_dict: Optional[Dict[Any, Any]], handler: Callable[[str, str], str] - ) -> Optional[Any]: + def _handle_sub_action(self, input_dict: dict[Any, Any] | None, handler: Callable[[str, str], str]) -> Any | None: """ Handles resolving replacements in the Sub action based on the handler that is passed as an input. @@ -436,13 +429,11 @@ class GetAttAction(Action): _MIN_NUM_ARGUMENTS = 2 - def resolve_parameter_refs(self, input_dict: Optional[Any], parameters: Dict[str, Any]) -> Optional[Any]: + def resolve_parameter_refs(self, input_dict: Any | None, parameters: dict[str, Any]) -> Any | None: # Parameters can never be referenced within GetAtt value return input_dict - def resolve_resource_refs( - self, input_dict: Optional[Any], supported_resource_refs: Dict[str, Any] - ) -> Optional[Any]: + def resolve_resource_refs(self, input_dict: Any | None, supported_resource_refs: dict[str, Any]) -> Any | None: """ Resolve resource references within a GetAtt dict. @@ -496,8 +487,8 @@ def resolve_resource_refs( return self._get_resolved_dictionary(input_dict, key, resolved_value, remaining) def resolve_resource_id_refs( - self, input_dict: Optional[Any], supported_resource_id_refs: Dict[str, Any] - ) -> Optional[Any]: + self, input_dict: Any | None, supported_resource_id_refs: dict[str, Any] + ) -> Any | None: """ Resolve resource references within a GetAtt dict. @@ -549,8 +540,8 @@ def _check_input_value(self, value: Any) -> bool: return all(isinstance(item, str) for item in value) def _get_resolved_dictionary( - self, input_dict: Optional[Dict[str, Any]], key: str, resolved_value: Optional[str], remaining: List[str] - ) -> Optional[Any]: + self, input_dict: dict[str, Any] | None, key: str, resolved_value: str | None, remaining: list[str] + ) -> Any | None: """ Resolves the function and returns the updated dictionary @@ -576,7 +567,7 @@ class FindInMapAction(Action): _NUM_ARGUMENTS = 3 - def resolve_parameter_refs(self, input_dict: Optional[Any], parameters: Dict[str, Any]) -> Optional[Any]: + def resolve_parameter_refs(self, input_dict: Any | None, parameters: dict[str, Any]) -> Any | None: """ Recursively resolves "Fn::FindInMap"references that are present in the mappings and returns the value. If it is not in mappings, this method simply returns the input unchanged. diff --git a/samtranslator/intrinsics/resolver.py b/samtranslator/intrinsics/resolver.py index e510ab40e9..c47218672e 100644 --- a/samtranslator/intrinsics/resolver.py +++ b/samtranslator/intrinsics/resolver.py @@ -1,5 +1,6 @@ # Help resolve intrinsic functions -from typing import Any, Callable, Dict, List, Optional, Union, cast +from collections.abc import Callable +from typing import Any, Union, cast from samtranslator.intrinsics.actions import Action, GetAttAction, RefAction, SubAction from samtranslator.intrinsics.resource_refs import SupportedResourceReferences @@ -10,7 +11,7 @@ class IntrinsicsResolver: - def __init__(self, parameters: Dict[str, Any], supported_intrinsics: Optional[Dict[str, Any]] = None) -> None: + def __init__(self, parameters: dict[str, Any], supported_intrinsics: dict[str, Any] | None = None) -> None: """ Instantiate the resolver :param dict parameters: Map of parameter names to their values @@ -48,8 +49,8 @@ def resolve_parameter_refs(self, _input: Any) -> Any: return self._traverse(_input, self.parameters, self._try_resolve_parameter_refs) def resolve_sam_resource_refs( - self, _input: Dict[str, Any], supported_resource_refs: SupportedResourceReferences - ) -> Dict[str, Any]: + self, _input: dict[str, Any], supported_resource_refs: SupportedResourceReferences + ) -> dict[str, Any]: """ Customers can provide a reference to a "derived" SAM resource such as Alias of a Function or Stage of an API resource. This method recursively walks the tree, converting all derived references to the real resource name, @@ -69,14 +70,14 @@ def resolve_sam_resource_refs( directly resolving references. In subsequent recursions, this will be a fragment of the CFN template. :param SupportedResourceReferences supported_resource_refs: Object that contains information about the resource references supported in this SAM template, along with the value they should resolve to. - :return list errors: List of dictionary containing information about invalid reference. Empty list otherwise + :return list errors: list of dictionary containing information about invalid reference. Empty list otherwise """ - # The _traverse() return type is the same as the input. Here the input is Dict[str, Any] + # The _traverse() return type is the same as the input. Here the input is dict[str, Any] return cast( - Dict[str, Any], self._traverse(_input, supported_resource_refs, self._try_resolve_sam_resource_refs) + dict[str, Any], self._traverse(_input, supported_resource_refs, self._try_resolve_sam_resource_refs) ) - def resolve_sam_resource_id_refs(self, _input: Dict[str, Any], supported_resource_id_refs: Dict[str, str]) -> Any: + def resolve_sam_resource_id_refs(self, _input: dict[str, Any], supported_resource_id_refs: dict[str, str]) -> Any: """ Some SAM resources have their logical ids mutated from the original id that the customer writes in the template. This method recursively walks the tree and updates these logical ids from the old value @@ -95,15 +96,15 @@ def resolve_sam_resource_id_refs(self, _input: Dict[str, Any], supported_resourc :param dict input: CFN template that needs resolution. This method will modify the input directly resolving references. In subsequent recursions, this will be a fragment of the CFN template. :param dict supported_resource_id_refs: Dictionary that maps old logical ids to new ones. - :return list errors: List of dictionary containing information about invalid reference. Empty list otherwise + :return list errors: list of dictionary containing information about invalid reference. Empty list otherwise """ return self._traverse(_input, supported_resource_id_refs, self._try_resolve_sam_resource_id_refs) def _traverse( self, input_value: Any, - resolution_data: Union[Dict[str, Any], SupportedResourceReferences], - resolver_method: Callable[[Dict[str, Any], Any], Any], + resolution_data: Union[dict[str, Any], SupportedResourceReferences], + resolver_method: Callable[[dict[str, Any], Any], Any], ) -> Any: """ Driver method that performs the actual traversal of input and calls the appropriate `resolver_method` when @@ -127,7 +128,7 @@ def _traverse( # Traversal Algorithm: # # Imagine the input dictionary/list as a tree. We are doing a Pre-Order tree traversal here where we first - # process the root node before going to its children. Dict and Lists are the only two iterable nodes. + # process the root node before going to its children. dict and Lists are the only two iterable nodes. # Everything else is a leaf node. # # We do a Pre-Order traversal to handle the case where `input` contains intrinsic function as its only child @@ -150,9 +151,9 @@ def _traverse( def _traverse_dict( self, - input_dict: Dict[str, Any], - resolution_data: Union[Dict[str, Any], SupportedResourceReferences], - resolver_method: Callable[[Dict[str, Any], Any], Any], + input_dict: dict[str, Any], + resolution_data: Union[dict[str, Any], SupportedResourceReferences], + resolver_method: Callable[[dict[str, Any], Any], Any], ) -> Any: """ Traverse a dictionary to resolve intrinsic functions on every value @@ -169,14 +170,14 @@ def _traverse_dict( def _traverse_list( self, - input_list: List[Any], - resolution_data: Union[Dict[str, Any], SupportedResourceReferences], - resolver_method: Callable[[Dict[str, Any], Any], Any], + input_list: list[Any], + resolution_data: Union[dict[str, Any], SupportedResourceReferences], + resolver_method: Callable[[dict[str, Any], Any], Any], ) -> Any: """ Traverse a list to resolve intrinsic functions on every element - :param input_list: List of input + :param input_list: list of input :param resolution_data: Data that the `resolver_method` needs to operate :param resolver_method: Method that can actually resolve an intrinsic function, if it detects one :return: Modified list with intrinsic functions resolved @@ -186,7 +187,7 @@ def _traverse_list( return input_list - def _try_resolve_parameter_refs(self, _input: Dict[str, Any], parameters: Dict[str, Any]) -> Any: + def _try_resolve_parameter_refs(self, _input: dict[str, Any], parameters: dict[str, Any]) -> Any: """ Try to resolve parameter references on the given input object. The object could be of any type. If the input is not in the format used by intrinsics (ie. dictionary with one key), input is returned @@ -204,7 +205,7 @@ def _try_resolve_parameter_refs(self, _input: Dict[str, Any], parameters: Dict[s return self.supported_intrinsics[function_type].resolve_parameter_refs(_input, parameters) def _try_resolve_sam_resource_refs( - self, _input: Dict[str, Any], supported_resource_refs: SupportedResourceReferences + self, _input: dict[str, Any], supported_resource_refs: SupportedResourceReferences ) -> Any: """ Try to resolve SAM resource references on the given template. If the given object looks like one of the @@ -223,7 +224,7 @@ def _try_resolve_sam_resource_refs( return self.supported_intrinsics[function_type].resolve_resource_refs(_input, supported_resource_refs) def _try_resolve_sam_resource_id_refs( - self, _input: Dict[str, Any], supported_resource_id_refs: Dict[str, str] + self, _input: dict[str, Any], supported_resource_id_refs: dict[str, str] ) -> Any: """ Try to resolve SAM resource id references on the given template. If the given object looks like one of the @@ -240,7 +241,7 @@ def _try_resolve_sam_resource_id_refs( function_type = next(iter(_input.keys())) return self.supported_intrinsics[function_type].resolve_resource_id_refs(_input, supported_resource_id_refs) - def _is_intrinsic_dict(self, _input: Dict[str, Any]) -> bool: + def _is_intrinsic_dict(self, _input: dict[str, Any]) -> bool: """ Can the _input represent an intrinsic function in it? diff --git a/samtranslator/intrinsics/resource_refs.py b/samtranslator/intrinsics/resource_refs.py index 59b42f51c7..24971f481e 100644 --- a/samtranslator/intrinsics/resource_refs.py +++ b/samtranslator/intrinsics/resource_refs.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any class SupportedResourceReferences: @@ -11,7 +11,7 @@ class SupportedResourceReferences: def __init__(self) -> None: # This is a two level map like: # { "LogicalId": {"Property": "Value"} } - self._refs: Dict[str, Dict[str, Any]] = {} + self._refs: dict[str, dict[str, Any]] = {} def add(self, logical_id, property_name, value): # type: ignore[no-untyped-def] """ diff --git a/samtranslator/metrics/method_decorator.py b/samtranslator/metrics/method_decorator.py index fa152b1b7f..32e271a0c0 100644 --- a/samtranslator/metrics/method_decorator.py +++ b/samtranslator/metrics/method_decorator.py @@ -4,8 +4,9 @@ import functools import logging +from collections.abc import Callable from datetime import datetime -from typing import Callable, Optional, TypeVar, Union, overload +from typing import TypeVar, Union, overload from typing_extensions import ParamSpec @@ -84,18 +85,16 @@ def _send_cw_metric(prefix, name, execution_time_ms, func, args): # type: ignor @overload def cw_timer( - *, name: Optional[str] = None, prefix: Optional[str] = None + *, name: str | None = None, prefix: str | None = None ) -> Callable[[Callable[_PT, _RT]], Callable[_PT, _RT]]: ... @overload -def cw_timer( - _func: Callable[_PT, _RT], name: Optional[str] = None, prefix: Optional[str] = None -) -> Callable[_PT, _RT]: ... +def cw_timer(_func: Callable[_PT, _RT], name: str | None = None, prefix: str | None = None) -> Callable[_PT, _RT]: ... def cw_timer( - _func: Optional[Callable[_PT, _RT]] = None, name: Optional[str] = None, prefix: Optional[str] = None + _func: Callable[_PT, _RT] | None = None, name: str | None = None, prefix: str | None = None ) -> Union[Callable[_PT, _RT], Callable[[Callable[_PT, _RT]], Callable[_PT, _RT]]]: """ A method decorator, that will calculate execution time of the decorated method, and store this information as a diff --git a/samtranslator/metrics/metrics.py b/samtranslator/metrics/metrics.py index 72da530b73..e9b318c060 100644 --- a/samtranslator/metrics/metrics.py +++ b/samtranslator/metrics/metrics.py @@ -5,7 +5,7 @@ import logging from abc import ABC, abstractmethod from datetime import datetime, timezone -from typing import Any, Dict, List, Optional, TypedDict, Union +from typing import Any, TypedDict, Union from samtranslator.internal.deprecation_control import deprecated @@ -16,7 +16,7 @@ class MetricsPublisher(ABC): """Interface for all MetricPublishers""" @abstractmethod - def publish(self, namespace: str, metrics: List["MetricDatum"]) -> None: + def publish(self, namespace: str, metrics: list["MetricDatum"]) -> None: """ Abstract method to publish all metrics to CloudWatch @@ -70,7 +70,7 @@ class DummyMetricsPublisher(MetricsPublisher): def __init__(self) -> None: MetricsPublisher.__init__(self) - def publish(self, namespace: str, metrics: List["MetricDatum"]) -> None: + def publish(self, namespace: str, metrics: list["MetricDatum"]) -> None: """Do not publish any metric, this is a dummy publisher used for offline use.""" LOG.debug(f"Dummy publisher ignoring {len(metrics)} metrices") @@ -99,8 +99,8 @@ def __init__( name: str, value: Union[int, float], unit: str, - dimensions: Optional[List["MetricDimension"]] = None, - timestamp: Optional[datetime] = None, + dimensions: list["MetricDimension"] | None = None, + timestamp: datetime | None = None, ) -> None: """ Constructor @@ -117,7 +117,7 @@ def __init__( self.dimensions = dimensions if dimensions else [] self.timestamp = timestamp if timestamp else datetime.now(timezone.utc) - def get_metric_data(self) -> Dict[str, Any]: + def get_metric_data(self) -> dict[str, Any]: return { "MetricName": self.name, "Value": self.value, @@ -134,7 +134,7 @@ class MetricDimension(TypedDict): class Metrics: def __init__( - self, namespace: str = "ServerlessTransform", metrics_publisher: Optional[MetricsPublisher] = None + self, namespace: str = "ServerlessTransform", metrics_publisher: MetricsPublisher | None = None ) -> None: """ Constructor @@ -143,7 +143,7 @@ def __init__( :param metrics_publisher: publisher to publish all metrics """ self.metrics_publisher = metrics_publisher if metrics_publisher else DummyMetricsPublisher() - self.metrics_cache: Dict[str, List[MetricDatum]] = {} + self.metrics_cache: dict[str, list[MetricDatum]] = {} self.namespace = namespace def __del__(self) -> None: @@ -159,8 +159,8 @@ def _record_metric( name: str, value: Union[int, float], unit: str, - dimensions: Optional[List["MetricDimension"]] = None, - timestamp: Optional[datetime] = None, + dimensions: list["MetricDimension"] | None = None, + timestamp: datetime | None = None, ) -> None: """ Create and save metric object in internal cache. @@ -177,8 +177,8 @@ def record_count( self, name: str, value: int, - dimensions: Optional[List["MetricDimension"]] = None, - timestamp: Optional[datetime] = None, + dimensions: list["MetricDimension"] | None = None, + timestamp: datetime | None = None, ) -> None: """ Create metric with unit Count. @@ -195,8 +195,8 @@ def record_latency( self, name: str, value: Union[int, float], - dimensions: Optional[List["MetricDimension"]] = None, - timestamp: Optional[datetime] = None, + dimensions: list["MetricDimension"] | None = None, + timestamp: datetime | None = None, ) -> None: """ Create metric with unit Milliseconds. @@ -219,11 +219,11 @@ def publish(self) -> None: self.metrics_publisher.publish(self.namespace, all_metrics) self.metrics_cache = {} - def get_metric(self, name: str) -> List[MetricDatum]: + def get_metric(self, name: str) -> list[MetricDatum]: """ Returns a list of metrics from the internal cache for a metric name :param name: metric name - :returns: List (possibly empty) of MetricDatum objects + :returns: list (possibly empty) of MetricDatum objects """ return self.metrics_cache.get(name, []) diff --git a/samtranslator/model/__init__.py b/samtranslator/model/__init__.py index be2682a2e1..239e94c25b 100644 --- a/samtranslator/model/__init__.py +++ b/samtranslator/model/__init__.py @@ -3,9 +3,10 @@ import inspect import re from abc import ABC, ABCMeta, abstractmethod +from collections.abc import Callable from contextlib import suppress from enum import Enum -from typing import Any, Callable, Dict, List, Optional, Tuple, Type, TypeVar +from typing import Any, TypeVar from samtranslator.compat import pydantic from samtranslator.model.exceptions import ( @@ -23,7 +24,7 @@ class StringEnumExpectedType: """Expected type wrapper for string enum validators.""" - def __init__(self, enum_values: List[str]): + def __init__(self, enum_values: list[str]): # Format description based on number of values num_values = len(enum_values) if num_values == 1: @@ -67,7 +68,7 @@ def __init__( self.supports_intrinsics = supports_intrinsics self.expected_type = self._resolve_expected_type(validate) - def _resolve_expected_type(self, validate: Validator) -> Optional[Any]: + def _resolve_expected_type(self, validate: Validator) -> Any | None: """Resolve expected_type from validator attribute or default mapping.""" # Check if validator has enum_values attribute (from IS_STR_ENUM) if hasattr(validate, "enum_values"): @@ -120,7 +121,7 @@ def __init__(self) -> None: class Resource(ABC): - """A Resource object represents an abstract entity that contains a Type and a Properties object. They map well to + """A Resource object represents an abstract entity that contains a type and a Properties object. They map well to CloudFormation resources as well sub-types like AWS::Lambda::Function or `Events` section of AWS::Serverless::Function. @@ -138,7 +139,7 @@ class Resource(ABC): # two lines to avoid any potential behavior change. # TODO: Make `Resource` an abstract class and not giving `resource_type`/`property_types` initial value. resource_type: str = None # type: ignore - property_types: Dict[str, PropertyType] = None # type: ignore + property_types: dict[str, PropertyType] = None # type: ignore _keywords = {"logical_id", "relative_id", "depends_on", "resource_attributes"} # For attributes in this list, they will be passed into the translated template for the same resource itself. @@ -154,20 +155,20 @@ class Resource(ABC): # attrs = { # "arn": fnGetAtt(self.logical_id, "Arn") # } - runtime_attrs: Dict[str, Callable[["Resource"], Any]] = {} # TODO: replace Any with something more explicit + runtime_attrs: dict[str, Callable[["Resource"], Any]] = {} # TODO: replace Any with something more explicit # When "validate_setattr" is True, we cannot change the value of any class variables after instantiation unless they # are in "property_types" or "_keywords". We can set this to False in the inheriting class definition so we can # update other class variables as well after instantiation. validate_setattr: bool = True - Tags: Optional[PassThrough] + Tags: PassThrough | None def __init__( self, - logical_id: Optional[Any], - relative_id: Optional[str] = None, - depends_on: Optional[List[str]] = None, - attributes: Optional[Dict[str, Any]] = None, + logical_id: Any | None, + relative_id: str | None = None, + depends_on: list[str] | None = None, + attributes: dict[str, Any] | None = None, ) -> None: """Initializes a Resource object with the given logical id. @@ -184,13 +185,13 @@ def __init__( for name, _ in self.property_types.items(): setattr(self, name, None) - self.resource_attributes: Dict[str, Any] = {} + self.resource_attributes: dict[str, Any] = {} if attributes is not None: for attr, value in attributes.items(): self.set_resource_attribute(attr, value) @classmethod - def get_supported_resource_attributes(cls) -> Tuple[str, ...]: + def get_supported_resource_attributes(cls) -> tuple[str, ...]: """ A getter method for the supported resource attributes returns: a tuple that contains the name of all supported resource attributes @@ -198,7 +199,7 @@ def get_supported_resource_attributes(cls) -> Tuple[str, ...]: return tuple(cls._supported_resource_attributes) @classmethod - def get_pass_through_attributes(cls) -> Tuple[str, ...]: + def get_pass_through_attributes(cls) -> tuple[str, ...]: """ A getter method for the resource attributes to be passed to auto-generated resources returns: a tuple that contains the name of all pass through attributes @@ -206,7 +207,7 @@ def get_pass_through_attributes(cls) -> Tuple[str, ...]: return tuple(cls._pass_through_attributes) @classmethod - def from_dict(cls, logical_id: str, resource_dict: Dict[str, Any], relative_id: Optional[str] = None, sam_plugins=None) -> "Resource": # type: ignore[no-untyped-def] + def from_dict(cls, logical_id: str, resource_dict: dict[str, Any], relative_id: str | None = None, sam_plugins=None) -> "Resource": # type: ignore[no-untyped-def] """Constructs a Resource object with the given logical id, based on the given resource dict. The resource dict is the value associated with the logical id in a CloudFormation template's Resources section, and takes the following format. :: @@ -220,7 +221,7 @@ def from_dict(cls, logical_id: str, resource_dict: Dict[str, Any], relative_id: :param str logical_id: The logical id of this Resource :param dict resource_dict: The value associated with this logical id in the CloudFormation template, a mapping \ - containing the resource's Type and Properties. + containing the resource's type and Properties. :param str relative_id: The logical id of this resource relative to the logical_id. This is useful to identify sub-resources. :param samtranslator.plugins.SamPlugins sam_plugins: Optional plugins object to help enhance functionality of @@ -257,7 +258,7 @@ def from_dict(cls, logical_id: str, resource_dict: Dict[str, Any], relative_id: return resource @staticmethod - def _validate_logical_id(logical_id: Optional[Any]) -> str: + def _validate_logical_id(logical_id: Any | None) -> str: """Validates that the provided logical id is an alphanumeric string. :param str logical_id: the logical id to validate @@ -274,8 +275,8 @@ def _validate_logical_id(logical_id: Optional[Any]) -> str: raise InvalidResourceException(str(logical_id), "Logical ids must be alphanumeric.") @classmethod - def _validate_resource_dict(cls, logical_id: str, resource_dict: Dict[str, Any]) -> None: - """Validates that the provided resource dict contains the correct Type string, and the required Properties dict. + def _validate_resource_dict(cls, logical_id: str, resource_dict: dict[str, Any]) -> None: + """Validates that the provided resource dict contains the correct type string, and the required Properties dict. :param dict resource_dict: the resource dict to validate :returns: True if the resource dict has the expected format @@ -287,14 +288,14 @@ def _validate_resource_dict(cls, logical_id: str, resource_dict: Dict[str, Any]) if resource_dict["Type"] != cls.resource_type: raise InvalidResourceException( logical_id, - "Resource has incorrect Type; expected '{expected}', " + "Resource has incorrect type; expected '{expected}', " "got '{actual}'".format(expected=cls.resource_type, actual=resource_dict["Type"]), ) if "Properties" in resource_dict and not isinstance(resource_dict["Properties"], dict): raise InvalidResourceException(logical_id, "Properties of a resource must be an object.") - def to_dict(self) -> Dict[str, Dict[str, Any]]: + def to_dict(self) -> dict[str, dict[str, Any]]: """Validates that the required properties for this Resource have been provided, then returns a dict corresponding to the given Resource object. This dict will take the format of a single entry in the Resources section of a CloudFormation template, and will take the following format. :: @@ -321,14 +322,14 @@ def to_dict(self) -> Dict[str, Dict[str, Any]]: return {self.logical_id: resource_dict} - def _generate_resource_dict(self) -> Dict[str, Any]: + def _generate_resource_dict(self) -> dict[str, Any]: """Generates the resource dict for this Resource, the value associated with the logical id in a CloudFormation template's Resources section. :returns: the resource dict for this Resource :rtype: dict """ - resource_dict: Dict[str, Any] = {"Type": self.resource_type} + resource_dict: dict[str, Any] = {"Type": self.resource_type} if self.depends_on: resource_dict["DependsOn"] = self.depends_on @@ -362,7 +363,7 @@ def __setattr__(self, name, value): # type: ignore[no-untyped-def] ) # Note: For compabitliy issue, we should ONLY use this with new abstraction/resources. - def validate_properties_and_return_model(self, cls: Type[RT], collect_all_errors: bool = False) -> RT: + def validate_properties_and_return_model(self, cls: type[RT], collect_all_errors: bool = False) -> RT: """ Given a resource properties, return a typed object from the definitions of SAM schema model @@ -382,7 +383,7 @@ def validate_properties_and_return_model(self, cls: Type[RT], collect_all_errors error_properties = ".".join(str(x) for x in e.errors()[0]["loc"]) raise InvalidResourceException(self.logical_id, f"Property '{error_properties}' is invalid.") from e - def _format_all_errors(self, errors: List[Dict[str, Any]]) -> List[str]: + def _format_all_errors(self, errors: list[dict[str, Any]]) -> list[str]: """Format all validation errors, consolidating union type errors in single pass.""" type_mapping = { "not a valid dict": "dictionary", @@ -393,7 +394,7 @@ def _format_all_errors(self, errors: List[Dict[str, Any]]) -> List[str]: } # Group errors by path in a single pass - path_to_errors: Dict[str, Dict[str, Any]] = {} + path_to_errors: dict[str, dict[str, Any]] = {} for error in errors: property_path = ".".join(str(x) for x in error["loc"]) @@ -427,7 +428,7 @@ def _format_all_errors(self, errors: List[Dict[str, Any]]) -> List[str]: return result - def _format_single_error(self, error: Dict[str, Any]) -> str: + def _format_single_error(self, error: dict[str, Any]) -> str: """Format a single Pydantic error into user-friendly message.""" property_path = ".".join(str(x) for x in error["loc"]) raw_message = error["msg"] @@ -511,7 +512,7 @@ def get_runtime_attr(self, attr_name: str) -> Any: return self.runtime_attrs[attr_name](self) - def get_passthrough_resource_attributes(self) -> Dict[str, Any]: + def get_passthrough_resource_attributes(self) -> dict[str, Any]: """ Returns a dictionary of resource attributes of the ResourceMacro that should be passed through from the main vanilla CloudFormation resource to its children. Currently only Condition is copied. @@ -524,7 +525,7 @@ def get_passthrough_resource_attributes(self) -> Dict[str, Any]: attributes[resource_attribute] = self.resource_attributes.get(resource_attribute) return attributes - def assign_tags(self, tags: Dict[str, Any]) -> None: + def assign_tags(self, tags: dict[str, Any]) -> None: """ Assigns tags to the resource. This function assumes that generated resources always have the tags property called `Tags` that takes a list of key-value objects. @@ -558,7 +559,7 @@ def resources_to_link(self, resources): # type: ignore[no-untyped-def] return {} @abstractmethod - def to_cloudformation(self, **kwargs: Any) -> List[Any]: + def to_cloudformation(self, **kwargs: Any) -> list[Any]: """Returns a list of Resource instances, representing vanilla CloudFormation resources, to which this macro expands. The caller should be able to update their template with the expanded resources by calling :func:`to_dict` on each resource returned, then updating their "Resources" mapping with the results. @@ -575,7 +576,7 @@ class ValidationRule(Enum): # Simple tuple-based rules: (rule_type, [property_names]) -PropertyRule = Tuple[ValidationRule, List[str]] +PropertyRule = tuple[ValidationRule, list[str]] class SamResourceMacro(ResourceMacro, metaclass=ABCMeta): @@ -592,7 +593,7 @@ class SamResourceMacro(ResourceMacro, metaclass=ABCMeta): # resources, there is a separate process that associates this property with LogicalId of the generated CFN resource # of the given type. - referable_properties: Dict[str, str] = {} + referable_properties: dict[str, str] = {} # Each resource can optionally override this tag: _SAM_KEY = "lambda:createdBy" @@ -611,7 +612,7 @@ def get_resource_references(self, generated_cfn_resources, supported_resource_re by to_cloudformation() on this SAM resource. Each SAM resource must provide a map of properties that it supports and the type of CFN resource this property resolves to. - :param list of Resource object generated_cfn_resources: List of CloudFormation resources generated by this + :param list of Resource object generated_cfn_resources: list of CloudFormation resources generated by this SAM resource :param samtranslator.intrinsics.resource_refs.SupportedResourceReferences supported_resource_refs: Object holding the mapping between property names and LogicalId of the generated CFN resource it maps to @@ -631,14 +632,14 @@ def get_resource_references(self, generated_cfn_resources, supported_resource_re return supported_resource_refs def _construct_tag_list( - self, tags: Optional[Dict[str, Any]], additional_tags: Optional[Dict[str, Any]] = None - ) -> List[Dict[str, Any]]: - tags_dict: Dict[str, Any] = tags or {} + self, tags: dict[str, Any] | None, additional_tags: dict[str, Any] | None = None + ) -> list[dict[str, Any]]: + tags_dict: dict[str, Any] = tags or {} if additional_tags is None: additional_tags = {} - # At this point tags is guaranteed to be a Dict[str, Any] since we set it to {} if it was falsy + # At this point tags is guaranteed to be a dict[str, Any] since we set it to {} if it was falsy for tag in self._RESERVED_TAGS: self._check_tag(tag, tags_dict) @@ -652,7 +653,7 @@ def _construct_tag_list( @staticmethod def propagate_tags_combine( - resources: List[Resource], tags: Optional[Dict[str, Any]], propagate_tags: Optional[bool] = False + resources: list[Resource], tags: dict[str, Any] | None, propagate_tags: bool | None = False ) -> None: """ Propagates tags to the resources @@ -664,7 +665,7 @@ def propagate_tags_combine( - Use this method for new resource if you want to assign combined tags, not replace. :param propagate_tags: Whether we should pass the tags to generated resources. - :param resources: List of generated resources + :param resources: list of generated resources :param tags: dictionary of tags to propagate to the resources. :return: None @@ -686,13 +687,13 @@ def propagate_tags_combine( @staticmethod def propagate_tags( - resources: List[Resource], tags: Optional[Dict[str, Any]], propagate_tags: Optional[bool] = False + resources: list[Resource], tags: dict[str, Any] | None, propagate_tags: bool | None = False ) -> None: """ Propagates tags to the resources. :param propagate_tags: Whether we should pass the tags to generated resources. - :param resources: List of generated resources + :param resources: list of generated resources :param tags: dictionary of tags to propagate to the resources. :return: None @@ -703,7 +704,7 @@ def propagate_tags( for resource in resources: resource.assign_tags(tags) - def _check_tag(self, reserved_tag_name: str, tags: Dict[str, Any]) -> None: + def _check_tag(self, reserved_tag_name: str, tags: dict[str, Any]) -> None: if reserved_tag_name in tags: raise InvalidResourceException( self.logical_id, @@ -713,7 +714,7 @@ def _check_tag(self, reserved_tag_name: str, tags: Dict[str, Any]) -> None: "input.", ) - def validate_before_transform(self, schema_class: Optional[Type[RT]], collect_all_errors: bool = False) -> None: + def validate_before_transform(self, schema_class: type[RT] | None, collect_all_errors: bool = False) -> None: if not hasattr(self, "__validation_rules__"): return @@ -751,7 +752,7 @@ def validate_before_transform(self, schema_class: Optional[Type[RT]], collect_al if error_messages: raise InvalidResourceException(self.logical_id, "\n".join(error_messages)) - def _combine_string(self, words: List[str]) -> str: + def _combine_string(self, words: list[str]) -> str: return ", ".join(words[:-1]) + (" and " + words[-1] if len(words) > 1 else words[0] if words else "") def _get_property_value(self, prop: str, validated_model: Any = None) -> Any: @@ -799,13 +800,13 @@ def __init__(self, *modules: Any) -> None: ): self.resource_types[resource_class.resource_type] = resource_class - def can_resolve(self, resource_dict: Dict[str, Any]) -> bool: + def can_resolve(self, resource_dict: dict[str, Any]) -> bool: if not isinstance(resource_dict, dict) or not isinstance(resource_dict.get("Type"), str): return False return resource_dict["Type"] in self.resource_types - def resolve_resource_type(self, resource_dict: Dict[str, Any]) -> Any: + def resolve_resource_type(self, resource_dict: dict[str, Any]) -> Any: """Returns the Resource class corresponding to the 'Type' key in the given resource dict. :param dict resource_dict: the resource dict to resolve @@ -814,7 +815,7 @@ def resolve_resource_type(self, resource_dict: Dict[str, Any]) -> Any: """ if not self.can_resolve(resource_dict): raise TypeError( - "Resource dict has missing or invalid value for key Type. Event Type is: {}.".format( + "Resource dict has missing or invalid value for key type. Event type is: {}.".format( resource_dict.get("Type") ) ) @@ -824,7 +825,7 @@ def resolve_resource_type(self, resource_dict: Dict[str, Any]) -> Any: class ResourceResolver: - def __init__(self, resources: Dict[str, Dict[str, Any]]) -> None: + def __init__(self, resources: dict[str, dict[str, Any]]) -> None: """ Instantiate the resolver :param dict resources: Map of resource @@ -834,11 +835,11 @@ def __init__(self, resources: Dict[str, Dict[str, Any]]) -> None: raise TypeError("'Resources' is either null or not a valid dictionary.") self.resources = resources - def get_all_resources(self) -> Dict[str, Any]: + def get_all_resources(self) -> dict[str, Any]: """Return a dictionary of all resources from the SAM template.""" return self.resources - def get_resource_by_logical_id(self, _input: str) -> Optional[Dict[str, Any]]: + def get_resource_by_logical_id(self, _input: str) -> dict[str, Any] | None: """ Recursively find resource with matching Logical ID that are present in the template and returns the value. If it is not in template, this method simply returns the input unchanged. @@ -852,7 +853,7 @@ def get_resource_by_logical_id(self, _input: str) -> Optional[Dict[str, Any]]: return self.resources.get(_input, None) -__all__: List[str] = [ +__all__: list[str] = [ "IS_DICT", "IS_STR", "MutatedPassThroughProperty", diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 53711bab99..6c19ac2a44 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -1,7 +1,7 @@ import logging from collections import namedtuple from dataclasses import dataclass -from typing import Any, Dict, List, Optional, Set, Tuple, Union, cast +from typing import Any, Union, cast from samtranslator.feature_toggle.feature_toggle import FeatureToggle from samtranslator.metrics.method_decorator import cw_timer @@ -77,15 +77,15 @@ @dataclass class ApiDomainResponse: - domain: Optional[ApiGatewayDomainName] - apigw_basepath_mapping_list: Optional[List[ApiGatewayBasePathMapping]] + domain: ApiGatewayDomainName | None + apigw_basepath_mapping_list: list[ApiGatewayBasePathMapping] | None recordset_group: Any @dataclass class ApiDomainResponseV2: - domain: Optional[ApiGatewayDomainNameV2] - apigw_basepath_mapping_list: Optional[List[ApiGatewayBasePathMappingV2]] + domain: ApiGatewayDomainNameV2 | None + apigw_basepath_mapping_list: list[ApiGatewayBasePathMappingV2] | None recordset_group: Any domain_access_association: Any @@ -100,15 +100,15 @@ class SharedApiUsagePlan: def __init__(self) -> None: self.usage_plan_shared = False - self.stage_keys_shared: List[str] = [] - self.api_stages_shared: List[str] = [] - self.depends_on_shared: List[str] = [] + self.stage_keys_shared: list[str] = [] + self.api_stages_shared: list[str] = [] + self.depends_on_shared: list[str] = [] # shared resource level attributes - self.conditions: Set[str] = set() + self.conditions: set[str] = set() self.any_api_without_condition = False - self.deletion_policy: Optional[str] = None - self.update_replace_policy: Optional[str] = None + self.deletion_policy: str | None = None + self.update_replace_policy: str | None = None def get_combined_resource_attributes(self, resource_attributes, conditions): # type: ignore[no-untyped-def] """ @@ -117,9 +117,9 @@ def get_combined_resource_attributes(self, resource_attributes, conditions): # Parameters ---------- - resource_attributes: Dict[str] + resource_attributes: dict[str] A dictionary of resource level attributes of the API resource - conditions: Dict[str] + conditions: dict[str] Conditions section of the template """ self._set_deletion_policy(resource_attributes.get("DeletionPolicy")) # type: ignore[no-untyped-call] @@ -186,42 +186,42 @@ class ApiGenerator: def __init__( # noqa: PLR0913 self, logical_id: str, - cache_cluster_enabled: Optional[Intrinsicable[bool]], - cache_cluster_size: Optional[Intrinsicable[str]], - variables: Optional[Dict[str, Any]], - depends_on: Optional[List[str]], - definition_body: Optional[Dict[str, Any]], - definition_uri: Optional[Intrinsicable[str]], - name: Optional[Intrinsicable[str]], - stage_name: Optional[Intrinsicable[str]], + cache_cluster_enabled: Intrinsicable[bool] | None, + cache_cluster_size: Intrinsicable[str] | None, + variables: dict[str, Any] | None, + depends_on: list[str] | None, + definition_body: dict[str, Any] | None, + definition_uri: Intrinsicable[str] | None, + name: Intrinsicable[str] | None, + stage_name: Intrinsicable[str] | None, shared_api_usage_plan: Any, template_conditions: Any, - merge_definitions: Optional[bool] = None, - tags: Optional[Dict[str, Any]] = None, - endpoint_configuration: Optional[Dict[str, Any]] = None, - method_settings: Optional[List[Any]] = None, - binary_media: Optional[List[Any]] = None, - minimum_compression_size: Optional[Intrinsicable[int]] = None, - disable_execute_api_endpoint: Optional[Intrinsicable[bool]] = None, - cors: Optional[Intrinsicable[str]] = None, - auth: Optional[Dict[str, Any]] = None, - gateway_responses: Optional[Dict[str, Any]] = None, - access_log_setting: Optional[Dict[str, Any]] = None, - canary_setting: Optional[Dict[str, Any]] = None, - tracing_enabled: Optional[Intrinsicable[bool]] = None, - resource_attributes: Optional[Dict[str, Any]] = None, - passthrough_resource_attributes: Optional[Dict[str, Any]] = None, - open_api_version: Optional[Intrinsicable[str]] = None, - models: Optional[Dict[str, Any]] = None, - domain: Optional[Dict[str, Any]] = None, - fail_on_warnings: Optional[Intrinsicable[bool]] = None, - description: Optional[Intrinsicable[str]] = None, - mode: Optional[Intrinsicable[str]] = None, - api_key_source_type: Optional[Intrinsicable[str]] = None, - always_deploy: Optional[bool] = False, - feature_toggle: Optional[FeatureToggle] = None, - policy: Optional[Union[Dict[str, Any], Intrinsicable[str]]] = None, - security_policy: Optional[Intrinsicable[str]] = None, + merge_definitions: bool | None = None, + tags: dict[str, Any] | None = None, + endpoint_configuration: dict[str, Any] | None = None, + method_settings: list[Any] | None = None, + binary_media: list[Any] | None = None, + minimum_compression_size: Intrinsicable[int] | None = None, + disable_execute_api_endpoint: Intrinsicable[bool] | None = None, + cors: Intrinsicable[str] | None = None, + auth: dict[str, Any] | None = None, + gateway_responses: dict[str, Any] | None = None, + access_log_setting: dict[str, Any] | None = None, + canary_setting: dict[str, Any] | None = None, + tracing_enabled: Intrinsicable[bool] | None = None, + resource_attributes: dict[str, Any] | None = None, + passthrough_resource_attributes: dict[str, Any] | None = None, + open_api_version: Intrinsicable[str] | None = None, + models: dict[str, Any] | None = None, + domain: dict[str, Any] | None = None, + fail_on_warnings: Intrinsicable[bool] | None = None, + description: Intrinsicable[str] | None = None, + mode: Intrinsicable[str] | None = None, + api_key_source_type: Intrinsicable[str] | None = None, + always_deploy: bool | None = False, + feature_toggle: FeatureToggle | None = None, + policy: Union[dict[str, Any], Intrinsicable[str]] | None = None, + security_policy: Intrinsicable[str] | None = None, ): """Constructs an API Generator class that generates API Gateway resources @@ -374,7 +374,7 @@ def _add_endpoint_extension(self) -> None: editor.add_disable_execute_api_endpoint_extension(self.disable_execute_api_endpoint) self.definition_body = editor.swagger - def _construct_body_s3_dict(self) -> Dict[str, Any]: + def _construct_body_s3_dict(self) -> dict[str, Any]: """Constructs the RestApi's `BodyS3Location property`_, from the SAM Api's DefinitionUri property. :returns: a BodyS3Location dict, containing the S3 Bucket, Key, and Version of the Swagger definition @@ -432,7 +432,7 @@ def _construct_deployment(self, rest_api: ApiGatewayRestApi) -> ApiGatewayDeploy return deployment def _construct_stage( - self, deployment: ApiGatewayDeployment, swagger: Optional[Dict[str, Any]], redeploy_restapi_parameters: Any + self, deployment: ApiGatewayDeployment, swagger: dict[str, Any] | None, redeploy_restapi_parameters: Any ) -> ApiGatewayStage: """Constructs and returns the ApiGateway Stage. @@ -545,20 +545,20 @@ def _construct_api_domain( # noqa: PLR0912, PLR0915 (too many branches/statemen self._set_optional_domain_properties(domain) - basepaths: Optional[List[str]] + basepaths: list[str] | None basepath_value = self.domain.get("BasePath") # Create BasepathMappings if self.domain.get("BasePath") and isinstance(basepath_value, str): basepaths = [basepath_value] elif self.domain.get("BasePath") and isinstance(basepath_value, list): - basepaths = cast(Optional[List[Any]], basepath_value) + basepaths = cast(list[Any] | None, basepath_value) else: basepaths = None # Boolean to allow/disallow symbols in BasePath property normalize_basepath = self.domain.get("NormalizeBasePath", True) - basepath_resource_list: List[ApiGatewayBasePathMapping] = [] + basepath_resource_list: list[ApiGatewayBasePathMapping] = [] if basepaths is None: basepath_mapping = self._create_basepath_mapping(api_domain_name, rest_api, None, None) @@ -654,12 +654,12 @@ def _construct_api_domain_v2( # noqa: PLR0915 self._set_optional_domain_properties(domain) - basepaths: Optional[List[str]] = self._get_basepaths() + basepaths: list[str] | None = self._get_basepaths() # Boolean to allow/disallow symbols in BasePath property normalize_basepath = self.domain.get("NormalizeBasePath", True) - basepath_resource_list: List[ApiGatewayBasePathMappingV2] = [] + basepath_resource_list: list[ApiGatewayBasePathMappingV2] = [] if basepaths is None: basepath_mapping = self._create_basepath_mapping_v2(domain_name_arn, rest_api) basepath_resource_list.extend([basepath_mapping]) @@ -724,14 +724,14 @@ def _construct_api_domain_v2( # noqa: PLR0915 return ApiDomainResponseV2(domain, basepath_resource_list, record_set_group, domain_access_association_resource) - def _get_basepaths(self) -> Optional[List[str]]: + def _get_basepaths(self) -> list[str] | None: if self.domain is None: return None basepath_value = self.domain.get("BasePath") if self.domain.get("BasePath") and isinstance(basepath_value, str): return [basepath_value] if self.domain.get("BasePath") and isinstance(basepath_value, list): - return cast(Optional[List[Any]], basepath_value) + return cast(list[Any] | None, basepath_value) return None def _set_optional_domain_properties(self, domain: Union[ApiGatewayDomainName, ApiGatewayDomainNameV2]) -> None: @@ -744,7 +744,7 @@ def _set_optional_domain_properties(self, domain: Union[ApiGatewayDomainName, Ap if self.domain.get("OwnershipVerificationCertificateArn", None): domain.OwnershipVerificationCertificateArn = self.domain["OwnershipVerificationCertificateArn"] - def _get_record_set_group(self, logical_id: str, route53: Dict[str, Any]) -> Route53RecordSetGroup: + def _get_record_set_group(self, logical_id: str, route53: dict[str, Any]) -> Route53RecordSetGroup: record_set_group = Route53RecordSetGroup(logical_id, attributes=self.passthrough_resource_attributes) if "HostedZoneId" in route53: record_set_group.HostedZoneId = route53.get("HostedZoneId") @@ -754,7 +754,7 @@ def _get_record_set_group(self, logical_id: str, route53: Dict[str, Any]) -> Rou return record_set_group def _construct_single_record_set_group( - self, domain: Dict[str, Any], api_domain_name: str, route53: Any + self, domain: dict[str, Any], api_domain_name: str, route53: Any ) -> Route53RecordSetGroup: hostedZoneId = route53.get("HostedZoneId") hostedZoneName = route53.get("HostedZoneName") @@ -773,8 +773,8 @@ def _construct_single_record_set_group( return record_set_group def _construct_record_sets_for_domain( - self, custom_domain_config: Dict[str, Any], api_domain_name: str, route53_config: Dict[str, Any] - ) -> List[Dict[str, Any]]: + self, custom_domain_config: dict[str, Any], api_domain_name: str, route53_config: dict[str, Any] + ) -> list[dict[str, Any]]: recordset_list = [] alias_target = self._construct_alias_target(custom_domain_config, api_domain_name, route53_config) recordset = {} @@ -795,13 +795,13 @@ def _construct_record_sets_for_domain( return recordset_list @staticmethod - def _update_route53_routing_policy_properties(route53_config: Dict[str, Any], recordset: Dict[str, Any]) -> None: + def _update_route53_routing_policy_properties(route53_config: dict[str, Any], recordset: dict[str, Any]) -> None: if route53_config.get("Region") is not None: recordset["Region"] = route53_config.get("Region") if route53_config.get("SetIdentifier") is not None: recordset["SetIdentifier"] = route53_config.get("SetIdentifier") - def _construct_alias_target(self, domain: Dict[str, Any], api_domain_name: str, route53: Any) -> Dict[str, Any]: + def _construct_alias_target(self, domain: dict[str, Any], api_domain_name: str, route53: Any) -> dict[str, Any]: alias_target = {} target_health = route53.get("EvaluateTargetHealth") @@ -824,8 +824,8 @@ def _create_basepath_mapping( self, api_domain_name: PassThrough, rest_api: ApiGatewayRestApi, - logical_id: Optional[str], - basepath: Optional[str], + logical_id: str | None, + basepath: str | None, ) -> ApiGatewayBasePathMapping: basepath_mapping: ApiGatewayBasePathMapping @@ -856,8 +856,8 @@ def _create_basepath_mapping_v2( @cw_timer(prefix="Generator", name="Api") def to_cloudformation( - self, redeploy_restapi_parameters: Optional[Any], route53_record_set_groups: Dict[str, Route53RecordSetGroup] - ) -> List[Resource]: + self, redeploy_restapi_parameters: Any | None, route53_record_set_groups: dict[str, Route53RecordSetGroup] + ) -> list[Resource]: """Generates CloudFormation resources from a SAM API resource :returns: a tuple containing the RestApi, Deployment, and Stage for an empty Api. @@ -865,7 +865,7 @@ def to_cloudformation( """ api_domain_response: Union[ApiDomainResponseV2, ApiDomainResponse] domain: Union[Resource, None] - basepath_mapping: Union[List[ApiGatewayBasePathMapping], List[ApiGatewayBasePathMappingV2], None] + basepath_mapping: Union[list[ApiGatewayBasePathMapping], list[ApiGatewayBasePathMappingV2], None] rest_api = self._construct_rest_api() is_private_domain = isinstance(self.domain, dict) and self.domain.get("EndpointConfiguration") == "PRIVATE" api_domain_response = ( @@ -895,16 +895,16 @@ def to_cloudformation( permissions = self._construct_authorizer_lambda_permission() usage_plan = self._construct_usage_plan(rest_api_stage=stage) - # mypy complains if the type in List doesn't match exactly + # mypy complains if the type in list doesn't match exactly # TODO: refactor to have a list of single resource - generated_resources: List[ + generated_resources: list[ Union[ - Optional[Resource], - List[Resource], - Tuple[Resource], - List[LambdaPermission], - List[ApiGatewayBasePathMapping], - List[ApiGatewayBasePathMappingV2], + Resource | None, + list[Resource], + tuple[Resource], + list[LambdaPermission], + list[ApiGatewayBasePathMapping], + list[ApiGatewayBasePathMappingV2], ], ] = [] @@ -925,7 +925,7 @@ def to_cloudformation( generated_resources.append(domain_access_association) # Make a list of single resources - generated_resources_list: List[Resource] = [] + generated_resources_list: list[Resource] = [] for resource in generated_resources: if resource: if isinstance(resource, (list, tuple)): @@ -1065,7 +1065,7 @@ def _add_auth(self) -> None: self.definition_body = self._openapi_postprocess(swagger_editor.swagger) - def _construct_usage_plan(self, rest_api_stage: Optional[ApiGatewayStage] = None) -> Any: # noqa: PLR0912 + def _construct_usage_plan(self, rest_api_stage: ApiGatewayStage | None = None) -> Any: # noqa: PLR0912 """Constructs and returns the ApiGateway UsagePlan, ApiGateway UsagePlanKey, ApiGateway ApiKey for Auth. :param model.apigateway.ApiGatewayStage stage: the stage of rest api @@ -1088,7 +1088,7 @@ def _construct_usage_plan(self, rest_api_stage: Optional[ApiGatewayStage] = None raise InvalidResourceException(self.logical_id, "Invalid property for 'UsagePlan'") create_usage_plan = usage_plan_properties.get("CreateUsagePlan") - usage_plan: Optional[ApiGatewayUsagePlan] = None + usage_plan: ApiGatewayUsagePlan | None = None api_key = None usage_plan_key = None @@ -1326,7 +1326,7 @@ def _add_models(self) -> None: self.definition_body = self._openapi_postprocess(swagger_editor.swagger) - def _openapi_postprocess(self, definition_body: Dict[str, Any]) -> Dict[str, Any]: # noqa: PLR0912 + def _openapi_postprocess(self, definition_body: dict[str, Any]) -> dict[str, Any]: # noqa: PLR0912 """ Convert definitions to openapi 3 in definition body if OpenApiVersion flag is specified. @@ -1472,7 +1472,7 @@ def _get_permission(self, authorizer_name, authorizer_lambda_function_arn): # t return lambda_permission - def _construct_authorizer_lambda_permission(self) -> List[LambdaPermission]: + def _construct_authorizer_lambda_permission(self) -> list[LambdaPermission]: if not self.auth: return [] @@ -1497,7 +1497,7 @@ def _construct_authorizer_lambda_permission(self) -> List[LambdaPermission]: def _set_default_authorizer( self, swagger_editor: SwaggerEditor, - authorizers: Dict[str, ApiGatewayAuthorizer], + authorizers: dict[str, ApiGatewayAuthorizer], default_authorizer: str, add_default_auth_to_preflight: bool = True, ) -> None: @@ -1530,7 +1530,7 @@ def _set_default_apikey_required(self, swagger_editor: SwaggerEditor, required_o for path in swagger_editor.iter_on_path(): swagger_editor.set_path_default_apikey_required(path, required_options_api_key) - def _set_endpoint_configuration(self, rest_api: ApiGatewayRestApi, value: Union[str, Dict[str, Any]]) -> None: + def _set_endpoint_configuration(self, rest_api: ApiGatewayRestApi, value: Union[str, dict[str, Any]]) -> None: """ Sets endpoint configuration property of AWS::ApiGateway::RestApi resource :param rest_api: RestApi resource @@ -1562,8 +1562,8 @@ def _set_endpoint_configuration(self, rest_api: ApiGatewayRestApi, value: Union[ def _generate_domain_access_association( self, - domain_access_association: Dict[str, Any], - domain_name_arn: Dict[str, str], + domain_access_association: dict[str, Any], + domain_name_arn: dict[str, str], domain_logical_id: str, ) -> ApiGatewayDomainNameAccessAssociation: """ diff --git a/samtranslator/model/api/http_api_generator.py b/samtranslator/model/api/http_api_generator.py index 1096fbd8f4..b2ac75256b 100644 --- a/samtranslator/model/api/http_api_generator.py +++ b/samtranslator/model/api/http_api_generator.py @@ -1,6 +1,6 @@ import re from collections import namedtuple -from typing import Any, Dict, List, Optional, Tuple, Union, cast +from typing import Any, Union, cast from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model.apigatewayv2 import ( @@ -38,24 +38,24 @@ class HttpApiGenerator: def __init__( # noqa: PLR0913 self, logical_id: str, - stage_variables: Optional[Dict[str, Intrinsicable[str]]], - depends_on: Optional[List[str]], - definition_body: Optional[Dict[str, Any]], - definition_uri: Optional[Intrinsicable[str]], - name: Optional[Any], - stage_name: Optional[Intrinsicable[str]], - tags: Optional[Dict[str, Intrinsicable[str]]] = None, - auth: Optional[Dict[str, Intrinsicable[str]]] = None, - cors_configuration: Optional[Union[bool, Dict[str, Any]]] = None, - access_log_settings: Optional[Dict[str, Intrinsicable[str]]] = None, - route_settings: Optional[Dict[str, Any]] = None, - default_route_settings: Optional[Dict[str, Any]] = None, - resource_attributes: Optional[Dict[str, Intrinsicable[str]]] = None, - passthrough_resource_attributes: Optional[Dict[str, Intrinsicable[str]]] = None, - domain: Optional[Dict[str, Any]] = None, - fail_on_warnings: Optional[Intrinsicable[bool]] = None, - description: Optional[Intrinsicable[str]] = None, - disable_execute_api_endpoint: Optional[Intrinsicable[bool]] = None, + stage_variables: dict[str, Intrinsicable[str]] | None, + depends_on: list[str] | None, + definition_body: dict[str, Any] | None, + definition_uri: Intrinsicable[str] | None, + name: Any | None, + stage_name: Intrinsicable[str] | None, + tags: dict[str, Intrinsicable[str]] | None = None, + auth: dict[str, Intrinsicable[str]] | None = None, + cors_configuration: Union[bool, dict[str, Any]] | None = None, + access_log_settings: dict[str, Intrinsicable[str]] | None = None, + route_settings: dict[str, Any] | None = None, + default_route_settings: dict[str, Any] | None = None, + resource_attributes: dict[str, Intrinsicable[str]] | None = None, + passthrough_resource_attributes: dict[str, Intrinsicable[str]] | None = None, + domain: dict[str, Any] | None = None, + fail_on_warnings: Intrinsicable[bool] | None = None, + description: Intrinsicable[str] | None = None, + disable_execute_api_endpoint: Intrinsicable[bool] | None = None, ) -> None: """Constructs an API Generator class that generates API Gateway resources @@ -236,16 +236,16 @@ def _update_default_path(self) -> None: # Warnings found during import: Parse issue: attribute paths. # Resource $default should start with / (Service: AmazonApiGatewayV2; Status Code: 400; # Deployment fails when FailOnWarnings is true: https://github.com/aws/serverless-application-model/issues/2297 - paths: Dict[str, Any] = self.definition_body.get("paths", {}) + paths: dict[str, Any] = self.definition_body.get("paths", {}) if DefaultStageName in paths: paths[f"/{DefaultStageName}"] = paths.pop(DefaultStageName) def _construct_api_domain( # noqa: PLR0912, PLR0915 - self, http_api: ApiGatewayV2HttpApi, route53_record_set_groups: Dict[str, Route53RecordSetGroup] - ) -> Tuple[ - Optional[ApiGatewayV2DomainName], - Optional[List[ApiGatewayV2ApiMapping]], - Optional[Route53RecordSetGroup], + self, http_api: ApiGatewayV2HttpApi, route53_record_set_groups: dict[str, Route53RecordSetGroup] + ) -> tuple[ + ApiGatewayV2DomainName | None, + list[ApiGatewayV2ApiMapping] | None, + Route53RecordSetGroup | None, ]: """ Constructs and returns the ApiGateway Domain and BasepathMapping @@ -323,12 +323,12 @@ def _construct_api_domain( # noqa: PLR0912, PLR0915 ) # Create BasepathMappings - basepaths: Optional[List[str]] + basepaths: list[str] | None basepath_value = self.domain.get("BasePath") if basepath_value and isinstance(basepath_value, str): basepaths = [basepath_value] elif basepath_value and isinstance(basepath_value, list): - basepaths = cast(Optional[List[str]], basepath_value) + basepaths = cast(list[str] | None, basepath_value) else: basepaths = None basepath_resource_list = self._construct_basepath_mappings(basepaths, http_api, api_domain_name) @@ -342,10 +342,10 @@ def _construct_api_domain( # noqa: PLR0912, PLR0915 def _construct_route53_recordsetgroup( self, - custom_domain_config: Dict[str, Any], - route53_record_set_groups: Dict[str, Route53RecordSetGroup], + custom_domain_config: dict[str, Any], + route53_record_set_groups: dict[str, Route53RecordSetGroup], api_domain_name: str, - ) -> Optional[Route53RecordSetGroup]: + ) -> Route53RecordSetGroup | None: route53_config = custom_domain_config.get("Route53") if route53_config is None: return None @@ -381,9 +381,9 @@ def _construct_route53_recordsetgroup( return record_set_group def _construct_basepath_mappings( - self, basepaths: Optional[List[str]], http_api: ApiGatewayV2HttpApi, api_domain_name: str - ) -> List[ApiGatewayV2ApiMapping]: - basepath_resource_list: List[ApiGatewayV2ApiMapping] = [] + self, basepaths: list[str] | None, http_api: ApiGatewayV2HttpApi, api_domain_name: str + ) -> list[ApiGatewayV2ApiMapping]: + basepath_resource_list: list[ApiGatewayV2ApiMapping] = [] if basepaths is None: basepath_mapping = ApiGatewayV2ApiMapping( @@ -415,8 +415,8 @@ def _construct_basepath_mappings( return basepath_resource_list def _construct_record_sets_for_domain( - self, custom_domain_config: Dict[str, Any], route53_config: Dict[str, Any], api_domain_name: str - ) -> List[Dict[str, Any]]: + self, custom_domain_config: dict[str, Any], route53_config: dict[str, Any], api_domain_name: str + ) -> list[dict[str, Any]]: recordset_list = [] recordset = {} @@ -439,15 +439,15 @@ def _construct_record_sets_for_domain( return recordset_list @staticmethod - def _update_route53_routing_policy_properties(route53_config: Dict[str, Any], recordset: Dict[str, Any]) -> None: + def _update_route53_routing_policy_properties(route53_config: dict[str, Any], recordset: dict[str, Any]) -> None: if route53_config.get("Region") is not None: recordset["Region"] = route53_config.get("Region") if route53_config.get("SetIdentifier") is not None: recordset["SetIdentifier"] = route53_config.get("SetIdentifier") def _construct_alias_target( - self, domain_config: Dict[str, Any], route53_config: Dict[str, Any], api_domain_name: str - ) -> Dict[str, Any]: + self, domain_config: dict[str, Any], route53_config: dict[str, Any], api_domain_name: str + ) -> dict[str, Any]: alias_target = {} target_health = route53_config.get("EvaluateTargetHealth") @@ -548,7 +548,7 @@ def _get_permission( return lambda_permission - def _construct_authorizer_lambda_permission(self, http_api: ApiGatewayV2HttpApi) -> List[LambdaPermission]: + def _construct_authorizer_lambda_permission(self, http_api: ApiGatewayV2HttpApi) -> list[LambdaPermission]: if not self.auth: return [] @@ -558,7 +558,7 @@ def _construct_authorizer_lambda_permission(self, http_api: ApiGatewayV2HttpApi) if not authorizers: return [] - permissions: List[LambdaPermission] = [] + permissions: list[LambdaPermission] = [] for authorizer_name, authorizer in authorizers.items(): # Construct permissions for Lambda Authorizers only @@ -580,8 +580,8 @@ def _construct_authorizer_lambda_permission(self, http_api: ApiGatewayV2HttpApi) def _set_default_authorizer( self, open_api_editor: OpenApiEditor, - authorizers: Dict[str, ApiGatewayV2Authorizer], - default_authorizer: Optional[Any], + authorizers: dict[str, ApiGatewayV2Authorizer], + default_authorizer: Any | None, ) -> None: """ Sets the default authorizer if one is given in the template @@ -611,13 +611,13 @@ def _set_default_authorizer( def _get_authorizers( self, authorizers_config: Any, enable_iam_authorizer: bool = False - ) -> Dict[str, ApiGatewayV2Authorizer]: + ) -> dict[str, ApiGatewayV2Authorizer]: """ Returns all authorizers for an API as an ApiGatewayV2Authorizer object :param authorizers_config: authorizer configuration from the API Auth section :param enable_iam_authorizer: if True add an "AWS_IAM" authorizer """ - authorizers: Dict[str, ApiGatewayV2Authorizer] = {} + authorizers: dict[str, ApiGatewayV2Authorizer] = {} if enable_iam_authorizer is True: authorizers["AWS_IAM"] = ApiGatewayV2Authorizer(is_aws_iam_authorizer=True) # type: ignore[no-untyped-call] @@ -651,7 +651,7 @@ def _get_authorizers( ) return authorizers - def _construct_body_s3_dict(self, definition_url: Union[str, Dict[str, Any]]) -> Dict[str, Any]: + def _construct_body_s3_dict(self, definition_url: Union[str, dict[str, Any]]) -> dict[str, Any]: """ Constructs the HttpApi's `BodyS3Location property`, from the SAM Api's DefinitionUri property. :returns: a BodyS3Location dict, containing the S3 Bucket, Key, and Version of the OpenApi definition @@ -681,7 +681,7 @@ def _construct_body_s3_dict(self, definition_url: Union[str, Dict[str, Any]]) -> body_s3["Version"] = s3_pointer["Version"] return body_s3 - def _construct_stage(self) -> Optional[ApiGatewayV2Stage]: + def _construct_stage(self) -> ApiGatewayV2Stage | None: """Constructs and returns the ApiGatewayV2 Stage. :returns: the Stage to which this SAM Api corresponds @@ -777,13 +777,13 @@ def _add_title(self) -> None: self.definition_body = open_api_editor.openapi @cw_timer(prefix="Generator", name="HttpApi") - def to_cloudformation(self, route53_record_set_groups: Dict[str, Route53RecordSetGroup]) -> Tuple[ + def to_cloudformation(self, route53_record_set_groups: dict[str, Route53RecordSetGroup]) -> tuple[ ApiGatewayV2HttpApi, - Optional[ApiGatewayV2Stage], - Optional[ApiGatewayV2DomainName], - Optional[List[ApiGatewayV2ApiMapping]], - Optional[Route53RecordSetGroup], - Optional[List[LambdaPermission]], + ApiGatewayV2Stage | None, + ApiGatewayV2DomainName | None, + list[ApiGatewayV2ApiMapping] | None, + Route53RecordSetGroup | None, + list[LambdaPermission] | None, ]: """Generates CloudFormation resources from a SAM HTTP API resource diff --git a/samtranslator/model/apigateway.py b/samtranslator/model/apigateway.py index 73966d6452..0cb27d2957 100644 --- a/samtranslator/model/apigateway.py +++ b/samtranslator/model/apigateway.py @@ -1,7 +1,7 @@ import json import time from re import match -from typing import Any, Dict, List, Optional, Union +from typing import Any, Union from samtranslator.model import GeneratedProperty, Resource from samtranslator.model.exceptions import InvalidResourceException @@ -33,21 +33,21 @@ class ApiGatewayRestApi(Resource): "SecurityPolicy": GeneratedProperty(), } - Body: Optional[Dict[str, Any]] - BodyS3Location: Optional[Dict[str, Any]] - CloneFrom: Optional[PassThrough] - Description: Optional[PassThrough] - FailOnWarnings: Optional[PassThrough] - Name: Optional[PassThrough] - Parameters: Optional[Dict[str, Any]] - EndpointConfiguration: Optional[Dict[str, Any]] - BinaryMediaTypes: Optional[List[Any]] - MinimumCompressionSize: Optional[PassThrough] - Mode: Optional[PassThrough] - ApiKeySourceType: Optional[PassThrough] - Tags: Optional[PassThrough] - Policy: Optional[PassThrough] - SecurityPolicy: Optional[PassThrough] + Body: dict[str, Any] | None + BodyS3Location: dict[str, Any] | None + CloneFrom: PassThrough | None + Description: PassThrough | None + FailOnWarnings: PassThrough | None + Name: PassThrough | None + Parameters: dict[str, Any] | None + EndpointConfiguration: dict[str, Any] | None + BinaryMediaTypes: list[Any] | None + MinimumCompressionSize: PassThrough | None + Mode: PassThrough | None + ApiKeySourceType: PassThrough | None + Tags: PassThrough | None + Policy: PassThrough | None + SecurityPolicy: PassThrough | None runtime_attrs = {"rest_api_id": lambda self: ref(self.logical_id)} @@ -96,14 +96,14 @@ class ApiGatewayDeployment(Resource): runtime_attrs = {"deployment_id": lambda self: ref(self.logical_id)} - def make_auto_deployable( # noqa: PLR0913 + def make_auto_deployable( self, stage: ApiGatewayStage, - openapi_version: Optional[Union[Dict[str, Any], str]] = None, - swagger: Optional[Dict[str, Any]] = None, - domain: Optional[Dict[str, Any]] = None, - redeploy_restapi_parameters: Optional[Any] = None, - always_deploy: Optional[bool] = False, + openapi_version: Union[dict[str, Any], str] | None = None, + swagger: dict[str, Any] | None = None, + domain: dict[str, Any] | None = None, + redeploy_restapi_parameters: Any | None = None, + always_deploy: bool | None = False, ) -> None: """ Sets up the resource such that it will trigger a re-deployment when Swagger changes or always_deploy is true @@ -152,9 +152,9 @@ class ApiGatewayResponse: def __init__( self, api_logical_id: str, - response_parameters: Optional[Dict[str, Any]] = None, - response_templates: Optional[PassThrough] = None, - status_code: Optional[str] = None, + response_parameters: dict[str, Any] | None = None, + response_templates: PassThrough | None = None, + status_code: str | None = None, ) -> None: if response_parameters: # response_parameters has been validated in ApiGenerator._add_gateway_responses() @@ -187,7 +187,7 @@ def generate_swagger(self) -> Py27Dict: return swagger - def _add_prefixes(self, response_parameters: Dict[str, Any]) -> Dict[str, str]: + def _add_prefixes(self, response_parameters: dict[str, Any]) -> dict[str, str]: GATEWAY_RESPONSE_PREFIX = "gatewayresponse." # applying Py27Dict as this is part of swagger prefixed_parameters = Py27Dict() @@ -224,14 +224,14 @@ class ApiGatewayDomainName(Resource): "OwnershipVerificationCertificateArn": GeneratedProperty(), } - RegionalCertificateArn: Optional[PassThrough] + RegionalCertificateArn: PassThrough | None DomainName: PassThrough - EndpointConfiguration: Optional[PassThrough] - MutualTlsAuthentication: Optional[Dict[str, Any]] - SecurityPolicy: Optional[PassThrough] - CertificateArn: Optional[PassThrough] - Tags: Optional[PassThrough] - OwnershipVerificationCertificateArn: Optional[PassThrough] + EndpointConfiguration: PassThrough | None + MutualTlsAuthentication: dict[str, Any] | None + SecurityPolicy: PassThrough | None + CertificateArn: PassThrough | None + Tags: PassThrough | None + OwnershipVerificationCertificateArn: PassThrough | None class ApiGatewayDomainNameV2(Resource): @@ -246,11 +246,11 @@ class ApiGatewayDomainNameV2(Resource): } DomainName: PassThrough - EndpointConfiguration: Optional[PassThrough] - SecurityPolicy: Optional[PassThrough] - CertificateArn: Optional[PassThrough] - Tags: Optional[PassThrough] - Policy: Optional[PassThrough] + EndpointConfiguration: PassThrough | None + SecurityPolicy: PassThrough | None + CertificateArn: PassThrough | None + Tags: PassThrough | None + Policy: PassThrough | None class ApiGatewayBasePathMapping(Resource): @@ -331,7 +331,7 @@ def __init__( # type: ignore[no-untyped-def]# noqa: PLR0913 user_pool_arn=None, function_arn=None, identity=None, - function_payload_type: Optional[str] = None, + function_payload_type: str | None = None, function_invoke_role=None, is_aws_iam_authorizer=False, authorization_scopes=None, @@ -374,7 +374,7 @@ def __init__( # type: ignore[no-untyped-def]# noqa: PLR0913 f"Authorizers.{name}.DisableFunctionDefaultPermissions", ).to_be_a_bool() - def _is_missing_identity_source(self, identity: Dict[str, Any]) -> bool: + def _is_missing_identity_source(self, identity: dict[str, Any]) -> bool: if not identity: return True @@ -452,7 +452,7 @@ def generate_swagger(self) -> Py27Dict: return swagger - def _get_identity_validation_expression(self) -> Optional[PassThrough]: + def _get_identity_validation_expression(self) -> PassThrough | None: return self.identity and self.identity.get("ValidationExpression") @staticmethod @@ -462,8 +462,8 @@ def _build_identity_source_item(item_prefix: str, prop_value: str) -> str: return Py27UniStr(item) return item - def _build_identity_source_item_array(self, prop_key: str, item_prefix: str) -> List[str]: - arr: List[str] = [] + def _build_identity_source_item_array(self, prop_key: str, item_prefix: str) -> list[str]: + arr: list[str] = [] prop_value_list = self.identity.get(prop_key) if prop_value_list: prop_path = f"Auth.Authorizers.{self.name}.Identity.{prop_key}" @@ -492,10 +492,10 @@ def _get_identity_source(self) -> str: return identity_source - def _get_user_pool_arn_array(self) -> List[PassThrough]: + def _get_user_pool_arn_array(self) -> list[PassThrough]: return self.user_pool_arn if isinstance(self.user_pool_arn, list) else [self.user_pool_arn] - def _get_swagger_header_name(self) -> Optional[str]: + def _get_swagger_header_name(self) -> str | None: authorizer_type = self._get_type() payload_type = self._get_function_payload_type() @@ -513,7 +513,7 @@ def _get_type(self) -> str: return "LAMBDA" - def _get_identity_header(self) -> Optional[str]: + def _get_identity_header(self) -> str | None: if self.identity and not isinstance(self.identity, dict): raise InvalidResourceException( self.api_logical_id, @@ -526,13 +526,13 @@ def _get_identity_header(self) -> Optional[str]: return self.identity.get("Header") # type: ignore[no-any-return] - def _get_reauthorize_every(self) -> Optional[PassThrough]: + def _get_reauthorize_every(self) -> PassThrough | None: if not self.identity: return None return self.identity.get("ReauthorizeEvery") - def _get_function_invoke_role(self) -> Optional[PassThrough]: + def _get_function_invoke_role(self) -> PassThrough | None: if not self.function_invoke_role or self.function_invoke_role == "NONE": return None @@ -551,7 +551,7 @@ def _get_swagger_authtype(self) -> str: def _get_function_payload_type(self) -> str: return "TOKEN" if not self.function_payload_type else self.function_payload_type - def _get_swagger_authorizer_type(self) -> Optional[str]: + def _get_swagger_authorizer_type(self) -> str | None: authorizer_type = self._get_type() if authorizer_type == "COGNITO_USER_POOLS": diff --git a/samtranslator/model/apigatewayv2.py b/samtranslator/model/apigatewayv2.py index cb9f363168..7993c88809 100644 --- a/samtranslator/model/apigatewayv2.py +++ b/samtranslator/model/apigatewayv2.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Union +from typing import Any, Union from samtranslator.model import GeneratedProperty, Resource from samtranslator.model.exceptions import ExpectedType, InvalidResourceException @@ -26,7 +26,7 @@ class ApiGatewayV2HttpApi(Resource): runtime_attrs = {"http_api_id": lambda self: ref(self.logical_id)} - def assign_tags(self, tags: Dict[str, Any]) -> None: + def assign_tags(self, tags: dict[str, Any]) -> None: """Overriding default 'assign_tags' function in Resource class Function to assign tags to the resource @@ -53,9 +53,9 @@ class ApiGatewayV2Stage(Resource): } runtime_attrs = {"stage_name": lambda self: ref(self.logical_id)} - Tags: Optional[PassThrough] + Tags: PassThrough | None - def assign_tags(self, tags: Dict[str, Any]) -> None: + def assign_tags(self, tags: dict[str, Any]) -> None: """Overriding default 'assign_tags' function in Resource class Function to assign tags to the resource @@ -76,11 +76,11 @@ class ApiGatewayV2DomainName(Resource): } DomainName: Intrinsicable[str] - DomainNameConfigurations: Optional[List[Dict[str, Any]]] - MutualTlsAuthentication: Optional[Dict[str, Any]] - Tags: Optional[PassThrough] + DomainNameConfigurations: list[dict[str, Any]] | None + MutualTlsAuthentication: dict[str, Any] | None + Tags: PassThrough | None - def assign_tags(self, tags: Dict[str, Any]) -> None: + def assign_tags(self, tags: dict[str, Any]) -> None: """Overriding default 'assign_tags' function in Resource class Function to assign tags to the resource @@ -103,7 +103,7 @@ class ApiGatewayV2ApiMapping(Resource): # https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-authorizers-authorizerid.html#apis-apiid-authorizers-authorizerid-model-jwtconfiguration # Change to TypedDict when we don't have to support Python 3.7 -JwtConfiguration = Dict[str, Union[str, List[str]]] +JwtConfiguration = dict[str, Union[str, list[str]]] class ApiGatewayV2Authorizer: @@ -128,9 +128,7 @@ def __init__( # type: ignore[no-untyped-def] # noqa: PLR0913 self.api_logical_id = api_logical_id self.name = name self.authorization_scopes = authorization_scopes - self.jwt_configuration: Optional[JwtConfiguration] = self._get_jwt_configuration( - jwt_configuration, api_logical_id - ) + self.jwt_configuration: JwtConfiguration | None = self._get_jwt_configuration(jwt_configuration, api_logical_id) self.id_source = id_source self.function_arn = function_arn self.function_invoke_role = function_invoke_role @@ -234,12 +232,12 @@ def _validate_lambda_authorizer(self) -> None: self.api_logical_id, f"{self.name} Lambda Authorizer must define 'AuthorizerPayloadFormatVersion'." ) - def generate_openapi(self) -> Dict[str, Any]: + def generate_openapi(self) -> dict[str, Any]: """ Generates OAS for the securitySchemes section """ authorizer_type = self._get_auth_type() - openapi: Dict[str, Any] + openapi: dict[str, Any] if authorizer_type == "AWS_IAM": openapi = { @@ -307,13 +305,13 @@ def generate_openapi(self) -> Dict[str, Any]: raise ValueError(f"Unexpected authorizer_type: {authorizer_type}") return openapi - def _get_function_invoke_role(self) -> Optional[PassThrough]: + def _get_function_invoke_role(self) -> PassThrough | None: if not self.function_invoke_role or self.function_invoke_role == "NONE": return None return self.function_invoke_role - def _get_identity_source(self, auth_identity: Dict[str, Any]) -> List[str]: + def _get_identity_source(self, auth_identity: dict[str, Any]) -> list[str]: """ Generate the list of identitySource using authorizer's Identity config by flatting them. For the format of identitySource, see: @@ -325,7 +323,7 @@ def _get_identity_source(self, auth_identity: Dict[str, Any]) -> List[str]: - prefix "$stageVariables." to all values in "StageVariables" - prefix "$context." to all values in "Context" """ - identity_source: List[str] = [] + identity_source: list[str] = [] identity_property_path = f"Authorizers.{self.name}.Identity" @@ -346,8 +344,8 @@ def _get_identity_source(self, auth_identity: Dict[str, Any]) -> List[str]: @staticmethod def _get_jwt_configuration( - props: Optional[Dict[str, Union[str, List[str]]]], api_logical_id: str - ) -> Optional[JwtConfiguration]: + props: dict[str, Union[str, list[str]]] | None, api_logical_id: str + ) -> JwtConfiguration | None: """Make sure that JWT configuration dict keys are lower case. ApiGatewayV2Authorizer doesn't create `AWS::ApiGatewayV2::Authorizer` but generates diff --git a/samtranslator/model/capacity_provider/generators.py b/samtranslator/model/capacity_provider/generators.py index b79e2bf5f4..037808be3f 100644 --- a/samtranslator/model/capacity_provider/generators.py +++ b/samtranslator/model/capacity_provider/generators.py @@ -2,7 +2,7 @@ AWS::Serverless::CapacityProvider resource transformer """ -from typing import Any, Dict, List, Optional +from typing import Any from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model import Resource @@ -50,13 +50,13 @@ def __init__(self, logical_id: str, **kwargs: Any) -> None: self.passthrough_resource_attributes = kwargs.get("passthrough_resource_attributes") @cw_timer(prefix="Generator", name="CapacityProvider") - def to_cloudformation(self) -> List[Resource]: + def to_cloudformation(self) -> list[Resource]: """ Transform the capacity provider configuration to CloudFormation resources - :returns: List of CloudFormation resources + :returns: list of CloudFormation resources """ - resources: List[Resource] = [] + resources: list[Resource] = [] # Create IAM roles if not provided; if not self.operator_role: @@ -127,7 +127,7 @@ def _ensure_permissions_config(self, capacity_provider: LambdaCapacityProvider) if getattr(capacity_provider, "PermissionsConfig", None) is None: capacity_provider.PermissionsConfig = {} - def _transform_instance_requirements(self) -> Dict[str, Any]: + def _transform_instance_requirements(self) -> dict[str, Any]: """ Transform the SAM InstanceRequirements to CloudFormation format """ @@ -144,7 +144,7 @@ def _transform_instance_requirements(self) -> Dict[str, Any]: return instance_requirements - def _transform_scaling_config(self) -> Dict[str, Any]: + def _transform_scaling_config(self) -> dict[str, Any]: """ Transform the SAM ScalingConfig to CloudFormation format """ @@ -172,12 +172,12 @@ def _transform_scaling_config(self) -> Dict[str, Any]: return scaling_config - def _transform_tags(self, additional_tags: Optional[Dict[str, Any]] = None) -> List[Dict[str, str]]: + def _transform_tags(self, additional_tags: dict[str, Any] | None = None) -> list[dict[str, str]]: """ Helper function to generate tags with automatic SAM tag :param additional_tags: Optional additional tags to include - :returns: List of tag dictionaries for CloudFormation + :returns: list of tag dictionaries for CloudFormation """ tags_dict = additional_tags.copy() if additional_tags else {} tags_dict["lambda:createdBy"] = "SAM" diff --git a/samtranslator/model/capacity_provider/resources.py b/samtranslator/model/capacity_provider/resources.py index 762c59348a..d178e4590c 100644 --- a/samtranslator/model/capacity_provider/resources.py +++ b/samtranslator/model/capacity_provider/resources.py @@ -2,7 +2,7 @@ AWS::Lambda::CapacityProvider resources for SAM """ -from typing import Any, Dict, List, Optional +from typing import Any from samtranslator.model import GeneratedProperty, Resource from samtranslator.model.intrinsics import fnGetAtt, ref @@ -25,13 +25,13 @@ class LambdaCapacityProvider(Resource): "KmsKeyArn": GeneratedProperty(), } - CapacityProviderName: Optional[Intrinsicable[str]] - VpcConfig: Dict[str, Any] - PermissionsConfig: Dict[str, Any] - Tags: Optional[List[Dict[str, Any]]] - InstanceRequirements: Optional[Dict[str, Any]] - CapacityProviderScalingConfig: Optional[Dict[str, Any]] - KmsKeyArn: Optional[Intrinsicable[str]] + CapacityProviderName: Intrinsicable[str] | None + VpcConfig: dict[str, Any] + PermissionsConfig: dict[str, Any] + Tags: list[dict[str, Any]] | None + InstanceRequirements: dict[str, Any] | None + CapacityProviderScalingConfig: dict[str, Any] | None + KmsKeyArn: Intrinsicable[str] | None runtime_attrs = { "name": lambda self: ref(self.logical_id), diff --git a/samtranslator/model/connector/connector.py b/samtranslator/model/connector/connector.py index 354bf740b7..cbc29ff264 100644 --- a/samtranslator/model/connector/connector.py +++ b/samtranslator/model/connector/connector.py @@ -1,7 +1,6 @@ from collections import namedtuple -from typing import Any, Dict, Iterable, List, Optional - -from typing_extensions import TypeGuard +from collections.abc import Iterable +from typing import Any, TypeGuard from samtranslator.model import ResourceResolver from samtranslator.model.apigateway import ApiGatewayRestApi @@ -66,7 +65,7 @@ def add_depends_on(logical_id: str, depends_on: str, resource_resolver: Resource resource["DependsOn"] = deps -def replace_depends_on_logical_id(logical_id: str, replacement: List[str], resource_resolver: ResourceResolver) -> None: +def replace_depends_on_logical_id(logical_id: str, replacement: list[str], resource_resolver: ResourceResolver) -> None: """ For every resource's `DependsOn`, replace `logical_id` by `replacement`. """ @@ -100,7 +99,7 @@ def get_event_source_mappings( yield logical_id -def _is_valid_resource_reference(obj: Dict[str, Any]) -> bool: +def _is_valid_resource_reference(obj: dict[str, Any]) -> bool: id_provided = "Id" in obj # Every property in ResourceReference can be implied using 'Id', except for 'Qualifier', so users should be able to combine 'Id' and 'Qualifier' non_id_provided = len([k for k in obj if k not in ["Id", "Qualifier"]]) > 0 @@ -109,7 +108,7 @@ def _is_valid_resource_reference(obj: Dict[str, Any]) -> bool: def get_resource_reference( - obj: Dict[str, Any], resource_resolver: ResourceResolver, connecting_obj: Dict[str, Any] + obj: dict[str, Any], resource_resolver: ResourceResolver, connecting_obj: dict[str, Any] ) -> ConnectorResourceReference: if not _is_valid_resource_reference(obj): raise ConnectorResourceError( @@ -182,8 +181,8 @@ def get_resource_reference( def _get_events_rule_role( - connecting_obj_id: Optional[str], connecting_obj_arn: Optional[Any], properties: Dict[str, Any] -) -> Optional[Any]: + connecting_obj_id: str | None, connecting_obj_arn: Any | None, properties: dict[str, Any] +) -> Any | None: for target in properties.get("Targets", []): target_arn = target.get("Arn") target_logical_id = get_logical_id_from_intrinsic(target_arn) @@ -195,10 +194,10 @@ def _get_events_rule_role( def _get_resource_role_property( - connecting_obj_id: Optional[str], - connecting_obj_arn: Optional[Any], - cfn_resource_properties: Dict[str, Any], - properties: Dict[str, Any], + connecting_obj_id: str | None, + connecting_obj_arn: Any | None, + cfn_resource_properties: dict[str, Any], + properties: dict[str, Any], ) -> Any: role_property = cfn_resource_properties.get("Inputs", {}).get("Role") @@ -212,10 +211,10 @@ def _get_resource_role_property( def _get_resource_role_name( - connecting_obj_id: Optional[str], - connecting_obj_arn: Optional[Any], - cfn_resource_properties: Dict[str, Any], - properties: Dict[str, Any], + connecting_obj_id: str | None, + connecting_obj_arn: Any | None, + cfn_resource_properties: dict[str, Any], + properties: dict[str, Any], ) -> Any: role = _get_resource_role_property(connecting_obj_id, connecting_obj_arn, cfn_resource_properties, properties) if not role: @@ -228,24 +227,24 @@ def _get_resource_role_name( return ref(logical_id) -def _get_resource_queue_url(properties: Dict[str, Any]) -> Optional[Any]: +def _get_resource_queue_url(properties: dict[str, Any]) -> Any | None: return properties.get("Url") -def _get_resource_id(properties: Dict[str, Any]) -> Optional[Any]: +def _get_resource_id(properties: dict[str, Any]) -> Any | None: return properties.get("Id") -def _get_resource_name(properties: Dict[str, Any]) -> Optional[Any]: +def _get_resource_name(properties: dict[str, Any]) -> Any | None: return properties.get("Name") -def _get_resource_qualifier(properties: Dict[str, Any]) -> Optional[Any]: +def _get_resource_qualifier(properties: dict[str, Any]) -> Any | None: # Qualifier is used as the execute-api ARN suffix; by default allow whole API return properties.get("Qualifier") -def _get_resource_arn(properties: Dict[str, Any]) -> Any: +def _get_resource_arn(properties: dict[str, Any]) -> Any: # according to documentation, Ref returns ARNs for these two resource types # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-stepfunctions-statemachine.html#aws-resource-stepfunctions-statemachine-return-values # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sns-topic.html#aws-resource-sns-topic-return-values diff --git a/samtranslator/model/connector_profiles/profile.py b/samtranslator/model/connector_profiles/profile.py index 75a30b1c01..b05ce0d96c 100644 --- a/samtranslator/model/connector_profiles/profile.py +++ b/samtranslator/model/connector_profiles/profile.py @@ -2,9 +2,9 @@ import json import re from pathlib import Path -from typing import Any, Dict +from typing import Any -ConnectorProfile = Dict[str, Any] +ConnectorProfile = dict[str, Any] _PROFILE_FILE = Path(__file__).absolute().parent / "profiles.json" with _PROFILE_FILE.open(encoding="utf-8") as f: @@ -33,7 +33,7 @@ def verify_profile_variables_replaced(obj: Any) -> None: raise ValueError(f"The following variables have not been replaced: {matches}") -def profile_replace(obj: Any, replacements: Dict[str, Any]): # type: ignore[no-untyped-def] +def profile_replace(obj: Any, replacements: dict[str, Any]): # type: ignore[no-untyped-def] """ This function is used to recursively replace all keys in 'replacements' found in 'obj' with matching values in 'replacement' dictionary. @@ -57,7 +57,7 @@ def _sanitize(s: str) -> str: return "".join(c for c in s if c.isalnum()) -def _profile_replace_str(s: Any, replacements: Dict[str, Any]): # type: ignore[no-untyped-def] +def _profile_replace_str(s: Any, replacements: dict[str, Any]): # type: ignore[no-untyped-def] if not isinstance(s, str): return s res = {} diff --git a/samtranslator/model/eventsources/cloudwatchlogs.py b/samtranslator/model/eventsources/cloudwatchlogs.py index aaca4c4b7d..5acc19404d 100644 --- a/samtranslator/model/eventsources/cloudwatchlogs.py +++ b/samtranslator/model/eventsources/cloudwatchlogs.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model import PropertyType @@ -40,7 +40,7 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] subscription_filter = self.get_subscription_filter(function, permission) # type: ignore[no-untyped-call] return [permission, subscription_filter] - def get_source_arn(self) -> Dict[str, Any]: + def get_source_arn(self) -> dict[str, Any]: resource = "log-group:${__LogGroupName__}:*" partition = ArnGenerator.get_partition_name() diff --git a/samtranslator/model/eventsources/pull.py b/samtranslator/model/eventsources/pull.py index 8719ac037c..dd1158d12f 100644 --- a/samtranslator/model/eventsources/pull.py +++ b/samtranslator/model/eventsources/pull.py @@ -1,5 +1,5 @@ from abc import ABCMeta, abstractmethod -from typing import Any, Dict, List, Optional, Tuple +from typing import Any from samtranslator.internal.deprecation_control import deprecated from samtranslator.intrinsics.resolver import IntrinsicsResolver @@ -36,7 +36,7 @@ class PullEventSource(ResourceMacro, metaclass=ABCMeta): # TODO: Make `PullEventSource` an abstract class and not giving `resource_type` initial value. resource_type: str = None # type: ignore relative_id: str # overriding the Optional[str]: for event, relative id is not None - property_types: Dict[str, PropertyType] = { + property_types: dict[str, PropertyType] = { "BatchSize": PropertyType(False, IS_INT), "StartingPosition": PassThroughProperty(False), "StartingPositionTimestamp": PassThroughProperty(False), @@ -64,44 +64,44 @@ class PullEventSource(ResourceMacro, metaclass=ABCMeta): "LoggingConfig": PropertyType(False, IS_DICT), } - BatchSize: Optional[Intrinsicable[int]] - StartingPosition: Optional[PassThrough] - StartingPositionTimestamp: Optional[PassThrough] - Enabled: Optional[bool] - MaximumBatchingWindowInSeconds: Optional[Intrinsicable[int]] - MaximumRetryAttempts: Optional[Intrinsicable[int]] - BisectBatchOnFunctionError: Optional[Intrinsicable[bool]] - MaximumRecordAgeInSeconds: Optional[Intrinsicable[int]] - DestinationConfig: Optional[Dict[str, Any]] - ParallelizationFactor: Optional[Intrinsicable[int]] - Topics: Optional[List[Any]] - Queues: Optional[List[Any]] - SourceAccessConfigurations: Optional[List[Any]] - SecretsManagerKmsKeyId: Optional[str] - TumblingWindowInSeconds: Optional[Intrinsicable[int]] - FunctionResponseTypes: Optional[List[Any]] - KafkaBootstrapServers: Optional[List[Any]] - FilterCriteria: Optional[Dict[str, Any]] - KmsKeyArn: Optional[Intrinsicable[str]] - ConsumerGroupId: Optional[Intrinsicable[str]] - ScalingConfig: Optional[Dict[str, Any]] - ProvisionedPollerConfig: Optional[Dict[str, Any]] - SchemaRegistryConfig: Optional[Dict[str, Any]] - MetricsConfig: Optional[Dict[str, Any]] - LoggingConfig: Optional[Dict[str, Any]] + BatchSize: Intrinsicable[int] | None + StartingPosition: PassThrough | None + StartingPositionTimestamp: PassThrough | None + Enabled: bool | None + MaximumBatchingWindowInSeconds: Intrinsicable[int] | None + MaximumRetryAttempts: Intrinsicable[int] | None + BisectBatchOnFunctionError: Intrinsicable[bool] | None + MaximumRecordAgeInSeconds: Intrinsicable[int] | None + DestinationConfig: dict[str, Any] | None + ParallelizationFactor: Intrinsicable[int] | None + Topics: list[Any] | None + Queues: list[Any] | None + SourceAccessConfigurations: list[Any] | None + SecretsManagerKmsKeyId: str | None + TumblingWindowInSeconds: Intrinsicable[int] | None + FunctionResponseTypes: list[Any] | None + KafkaBootstrapServers: list[Any] | None + FilterCriteria: dict[str, Any] | None + KmsKeyArn: Intrinsicable[str] | None + ConsumerGroupId: Intrinsicable[str] | None + ScalingConfig: dict[str, Any] | None + ProvisionedPollerConfig: dict[str, Any] | None + SchemaRegistryConfig: dict[str, Any] | None + MetricsConfig: dict[str, Any] | None + LoggingConfig: dict[str, Any] | None @abstractmethod - def get_policy_arn(self) -> Optional[str]: + def get_policy_arn(self) -> str | None: """Policy to be added to the role (if a role applies).""" @abstractmethod def get_policy_statements( - self, intrinsic_resolver: Optional[IntrinsicsResolver] = None - ) -> Optional[List[Dict[str, Any]]]: + self, intrinsic_resolver: IntrinsicsResolver | None = None + ) -> list[dict[str, Any]] | None: """Inline policy statements to be added to the role (if a role applies).""" @abstractmethod - def get_event_source_arn(self) -> Optional[PassThrough]: + def get_event_source_arn(self) -> PassThrough | None: """Return the value to assign to lambda event source mapping's EventSourceArn.""" def add_extra_eventsourcemapping_fields(self, _lambda_eventsourcemapping: LambdaEventSourceMapping) -> None: @@ -192,9 +192,9 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: P lambda_eventsourcemapping.SelfManagedKafkaEventSourceConfig["SchemaRegistryConfig"] = ( # type: ignore[attr-defined] self.SchemaRegistryConfig ) - destination_config_policy: Optional[Dict[str, Any]] = None + destination_config_policy: dict[str, Any] | None = None if self.DestinationConfig: - on_failure: Dict[str, Any] = sam_expect( + on_failure: dict[str, Any] = sam_expect( self.DestinationConfig.get("OnFailure"), self.logical_id, "DestinationConfig.OnFailure", @@ -291,7 +291,7 @@ def validate_secrets_manager_kms_key_id(self) -> None: self.SecretsManagerKmsKeyId, self.relative_id, "SecretsManagerKmsKeyId", is_sam_event=True ).to_be_a_string() - def _validate_source_access_configurations(self, supported_types: List[str], required_type: str) -> str: + def _validate_source_access_configurations(self, supported_types: list[str], required_type: str) -> str: """ Validate the SourceAccessConfigurations parameter and return the URI to be used for policy statement creation. @@ -308,7 +308,7 @@ def _validate_source_access_configurations(self, supported_types: List[str], req "Provided SourceAccessConfigurations cannot be parsed into a list.", ) - required_type_uri: Optional[str] = None + required_type_uri: str | None = None for index, conf in enumerate(self.SourceAccessConfigurations): sam_expect(conf, self.relative_id, f"SourceAccessConfigurations[{index}]", is_sam_event=True).to_be_a_map() event_type: str = sam_expect( @@ -340,7 +340,7 @@ def _validate_source_access_configurations(self, supported_types: List[str], req return required_type_uri @staticmethod - def _get_kms_decrypt_policy(secrets_manager_kms_key_id: str) -> Dict[str, Any]: + def _get_kms_decrypt_policy(secrets_manager_kms_key_id: str) -> dict[str, Any]: return { "Action": ["kms:Decrypt"], "Effect": "Allow", @@ -381,8 +381,8 @@ def validate_schema_registry_config(self) -> None: ) def get_schema_registry_permissions( - self, intrinsic_resolver: Optional[IntrinsicsResolver] = None - ) -> Optional[List[Dict[str, Any]]]: + self, intrinsic_resolver: IntrinsicsResolver | None = None + ) -> list[dict[str, Any]] | None: if not self.SchemaRegistryConfig: return None @@ -434,7 +434,7 @@ def get_schema_registry_permissions( ) return statements - def get_registry_name(self, registry_uri: str) -> Optional[str]: + def get_registry_name(self, registry_uri: str) -> str | None: if isinstance(registry_uri, str) and registry_uri.startswith("arn"): parts = registry_uri.split(":") if len(parts) >= PullEventSource.ARN_SEGMENTS_COUNT and parts[ @@ -448,7 +448,7 @@ class Kinesis(PullEventSource): """Kinesis event source.""" resource_type = "Kinesis" - property_types: Dict[str, PropertyType] = { + property_types: dict[str, PropertyType] = { **PullEventSource.property_types, "Stream": PassThroughProperty(True), "StartingPosition": PassThroughProperty(True), @@ -456,15 +456,15 @@ class Kinesis(PullEventSource): Stream: PassThrough - def get_event_source_arn(self) -> Optional[PassThrough]: + def get_event_source_arn(self) -> PassThrough | None: return self.Stream - def get_policy_arn(self) -> Optional[str]: + def get_policy_arn(self) -> str | None: return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaKinesisExecutionRole") def get_policy_statements( - self, intrinsic_resolver: Optional[IntrinsicsResolver] = None - ) -> Optional[List[Dict[str, Any]]]: + self, intrinsic_resolver: IntrinsicsResolver | None = None + ) -> list[dict[str, Any]] | None: return None @@ -472,7 +472,7 @@ class DynamoDB(PullEventSource): """DynamoDB Streams event source.""" resource_type = "DynamoDB" - property_types: Dict[str, PropertyType] = { + property_types: dict[str, PropertyType] = { **PullEventSource.property_types, "Stream": PassThroughProperty(True), "StartingPosition": PassThroughProperty(True), @@ -480,15 +480,15 @@ class DynamoDB(PullEventSource): Stream: PassThrough - def get_event_source_arn(self) -> Optional[PassThrough]: + def get_event_source_arn(self) -> PassThrough | None: return self.Stream - def get_policy_arn(self) -> Optional[str]: + def get_policy_arn(self) -> str | None: return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaDynamoDBExecutionRole") def get_policy_statements( - self, intrinsic_resolver: Optional[IntrinsicsResolver] = None - ) -> Optional[List[Dict[str, Any]]]: + self, intrinsic_resolver: IntrinsicsResolver | None = None + ) -> list[dict[str, Any]] | None: return None @@ -496,22 +496,22 @@ class SQS(PullEventSource): """SQS Queue event source.""" resource_type = "SQS" - property_types: Dict[str, PropertyType] = { + property_types: dict[str, PropertyType] = { **PullEventSource.property_types, "Queue": PassThroughProperty(True), } Queue: PassThrough - def get_event_source_arn(self) -> Optional[PassThrough]: + def get_event_source_arn(self) -> PassThrough | None: return self.Queue - def get_policy_arn(self) -> Optional[str]: + def get_policy_arn(self) -> str | None: return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaSQSQueueExecutionRole") def get_policy_statements( - self, intrinsic_resolver: Optional[IntrinsicsResolver] = None - ) -> Optional[List[Dict[str, Any]]]: + self, intrinsic_resolver: IntrinsicsResolver | None = None + ) -> list[dict[str, Any]] | None: return None @@ -519,7 +519,7 @@ class MSK(PullEventSource): """MSK event source.""" resource_type = "MSK" - property_types: Dict[str, PropertyType] = { + property_types: dict[str, PropertyType] = { **PullEventSource.property_types, "Stream": PassThroughProperty(True), "StartingPosition": PassThroughProperty(True), @@ -527,16 +527,16 @@ class MSK(PullEventSource): Stream: PassThrough - def get_event_source_arn(self) -> Optional[PassThrough]: + def get_event_source_arn(self) -> PassThrough | None: return self.Stream - def get_policy_arn(self) -> Optional[str]: + def get_policy_arn(self) -> str | None: return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaMSKExecutionRole") def get_policy_statements( - self, intrinsic_resolver: Optional[IntrinsicsResolver] = None - ) -> Optional[List[Dict[str, Any]]]: - statements: List[Dict[str, Any]] = [] + self, intrinsic_resolver: IntrinsicsResolver | None = None + ) -> list[dict[str, Any]] | None: + statements: list[dict[str, Any]] = [] if self.SchemaRegistryConfig: schema_registry_statements = self.get_schema_registry_permissions(intrinsic_resolver) if schema_registry_statements is not None: @@ -563,14 +563,14 @@ class MQ(PullEventSource): """MQ event source.""" resource_type = "MQ" - property_types: Dict[str, PropertyType] = { + property_types: dict[str, PropertyType] = { **PullEventSource.property_types, "Broker": PassThroughProperty(True), "DynamicPolicyName": Property(False, IS_BOOL), } Broker: PassThrough - DynamicPolicyName: Optional[bool] + DynamicPolicyName: bool | None @property def _policy_name(self) -> str: @@ -607,15 +607,15 @@ def _policy_name(self) -> str: """ return f"{self.logical_id}AMQPolicy" if self.DynamicPolicyName else "SamAutoGeneratedAMQPolicy" - def get_event_source_arn(self) -> Optional[PassThrough]: + def get_event_source_arn(self) -> PassThrough | None: return self.Broker - def get_policy_arn(self) -> Optional[str]: + def get_policy_arn(self) -> str | None: return None def get_policy_statements( - self, intrinsic_resolver: Optional[IntrinsicsResolver] = None - ) -> Optional[List[Dict[str, Any]]]: + self, intrinsic_resolver: IntrinsicsResolver | None = None + ) -> list[dict[str, Any]] | None: basic_auth_uri = self._validate_source_access_configurations(["BASIC_AUTH", "VIRTUAL_HOST"], "BASIC_AUTH") document = { @@ -666,15 +666,15 @@ class SelfManagedKafka(PullEventSource): "CLIENT_CERTIFICATE_TLS_AUTH", ] - def get_event_source_arn(self) -> Optional[PassThrough]: + def get_event_source_arn(self) -> PassThrough | None: return None - def get_policy_arn(self) -> Optional[str]: + def get_policy_arn(self) -> str | None: return None def get_policy_statements( - self, intrinsic_resolver: Optional[IntrinsicsResolver] = None - ) -> Optional[List[Dict[str, Any]]]: + self, intrinsic_resolver: IntrinsicsResolver | None = None + ) -> list[dict[str, Any]] | None: if not self.KafkaBootstrapServers: raise InvalidEventException( self.relative_id, @@ -702,7 +702,7 @@ def get_policy_statements( return [document] def generate_policy_document( # type: ignore[no-untyped-def] - self, source_access_configurations: List[Any], intrinsic_resolver: Optional[IntrinsicsResolver] = None + self, source_access_configurations: list[Any], intrinsic_resolver: IntrinsicsResolver | None = None ): statements = [] authentication_uri, authentication_uri_2, has_vpc_config = self.get_secret_key(source_access_configurations) @@ -737,7 +737,7 @@ def generate_policy_document( # type: ignore[no-untyped-def] "PolicyName": "SelfManagedKafkaExecutionRolePolicy", } - def get_secret_key(self, source_access_configurations: List[Any]) -> Tuple[Optional[str], Optional[str], bool]: + def get_secret_key(self, source_access_configurations: list[Any]) -> tuple[str | None, str | None, bool]: authentication_uri = None has_vpc_subnet = False has_vpc_security_group = False @@ -784,7 +784,7 @@ def get_secret_key(self, source_access_configurations: List[Any]) -> Tuple[Optio ) return authentication_uri, authentication_uri_2, (has_vpc_subnet and has_vpc_security_group) - def validate_uri(self, uri: Optional[Any], msg: str) -> None: + def validate_uri(self, uri: Any | None, msg: str) -> None: if not uri: raise InvalidEventException( self.relative_id, @@ -804,7 +804,7 @@ def get_secret_manager_secret(self, authentication_uri): # type: ignore[no-unty "Resource": authentication_uri, } - def get_vpc_permission(self) -> Dict[str, Any]: + def get_vpc_permission(self) -> dict[str, Any]: return { "Action": [ "ec2:CreateNetworkInterface", @@ -820,7 +820,7 @@ def get_vpc_permission(self) -> Dict[str, Any]: @staticmethod @deprecated() - def get_kms_policy(secrets_manager_kms_key_id: str) -> Dict[str, Any]: + def get_kms_policy(secrets_manager_kms_key_id: str) -> dict[str, Any]: return { "Action": ["kms:Decrypt"], "Effect": "Allow", @@ -835,7 +835,7 @@ class DocumentDB(PullEventSource): """DocumentDB event source.""" resource_type = "DocumentDB" - property_types: Dict[str, PropertyType] = { + property_types: dict[str, PropertyType] = { **PullEventSource.property_types, "Cluster": PassThroughProperty(True), "DatabaseName": PassThroughProperty(True), @@ -845,8 +845,8 @@ class DocumentDB(PullEventSource): Cluster: PassThrough DatabaseName: PassThrough - CollectionName: Optional[PassThrough] - FullDocument: Optional[PassThrough] + CollectionName: PassThrough | None + FullDocument: PassThrough | None def add_extra_eventsourcemapping_fields(self, lambda_eventsourcemapping: LambdaEventSourceMapping) -> None: lambda_eventsourcemapping.DocumentDBEventSourceConfig = { @@ -857,13 +857,13 @@ def add_extra_eventsourcemapping_fields(self, lambda_eventsourcemapping: LambdaE if self.FullDocument: lambda_eventsourcemapping.DocumentDBEventSourceConfig["FullDocument"] = self.FullDocument # type: ignore[attr-defined] - def get_event_source_arn(self) -> Optional[PassThrough]: + def get_event_source_arn(self) -> PassThrough | None: return self.Cluster - def get_policy_arn(self) -> Optional[str]: + def get_policy_arn(self) -> str | None: return None - def get_policy_statements(self, intrinsic_resolver: Optional[IntrinsicsResolver] = None) -> List[Dict[str, Any]]: + def get_policy_statements(self, intrinsic_resolver: IntrinsicsResolver | None = None) -> list[dict[str, Any]]: basic_auth_uri = self._validate_source_access_configurations(["BASIC_AUTH"], "BASIC_AUTH") statements = [ diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index 8f237f5270..425b2af745 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -1,7 +1,7 @@ import copy import re from abc import ABCMeta -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Union, cast from samtranslator.intrinsics.resolver import IntrinsicsResolver from samtranslator.metrics.method_decorator import cw_timer @@ -69,7 +69,7 @@ class PushEventSource(ResourceMacro, metaclass=ABCMeta): principal: str = None # type: ignore relative_id: str # overriding the Optional[str]: for event, relative id is not None - def _construct_permission( # type: ignore[no-untyped-def] # noqa: PLR0913 + def _construct_permission( # type: ignore[no-untyped-def] self, function, source_arn=None, source_account=None, suffix="", event_source_token=None, prefix=None ): """Constructs the Lambda Permission resource allowing the source service to invoke the function this event @@ -122,14 +122,14 @@ class Schedule(PushEventSource): } Schedule: PassThrough - RuleName: Optional[PassThrough] - Input: Optional[PassThrough] - Enabled: Optional[bool] - State: Optional[PassThrough] - Name: Optional[PassThrough] - Description: Optional[PassThrough] - DeadLetterConfig: Optional[Dict[str, Any]] - RetryPolicy: Optional[PassThrough] + RuleName: PassThrough | None + Input: PassThrough | None + Enabled: bool | None + State: PassThrough | None + Name: PassThrough | None + Description: PassThrough | None + DeadLetterConfig: dict[str, Any] | None + RetryPolicy: PassThrough | None @cw_timer(prefix=FUNCTION_EVETSOURCE_METRIC_PREFIX) def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] @@ -218,17 +218,17 @@ class CloudWatchEvent(PushEventSource): "InputTransformer": PropertyType(False, IS_DICT), } - EventBusName: Optional[PassThrough] - RuleName: Optional[PassThrough] - Pattern: Optional[PassThrough] - DeadLetterConfig: Optional[Dict[str, Any]] - RetryPolicy: Optional[PassThrough] - Input: Optional[PassThrough] - InputPath: Optional[PassThrough] - Target: Optional[PassThrough] - Enabled: Optional[bool] - State: Optional[PassThrough] - InputTransformer: Optional[PassThrough] + EventBusName: PassThrough | None + RuleName: PassThrough | None + Pattern: PassThrough | None + DeadLetterConfig: dict[str, Any] | None + RetryPolicy: PassThrough | None + Input: PassThrough | None + InputPath: PassThrough | None + Target: PassThrough | None + Enabled: bool | None + State: PassThrough | None + InputTransformer: PassThrough | None @cw_timer(prefix=FUNCTION_EVETSOURCE_METRIC_PREFIX) def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] @@ -324,9 +324,9 @@ class S3(PushEventSource): "Filter": PropertyType(False, dict_of(IS_STR, IS_STR)), } - Bucket: Dict[str, Any] - Events: Union[str, List[str]] - Filter: Optional[Dict[str, str]] + Bucket: dict[str, Any] + Events: Union[str, list[str]] + Filter: dict[str, str] | None def resources_to_link(self, resources): # type: ignore[no-untyped-def] if isinstance(self.Bucket, dict) and "Ref" in self.Bucket: @@ -417,8 +417,8 @@ def _depend_on_lambda_permissions(self, bucket, permission): # type: ignore[no- return bucket def _depend_on_lambda_permissions_using_tag( - self, bucket: Dict[str, Any], bucket_id: str, permission: LambdaPermission - ) -> Dict[str, Any]: + self, bucket: dict[str, Any], bucket_id: str, permission: LambdaPermission + ) -> dict[str, Any]: """ Since conditional DependsOn is not supported this undocumented way of implicitely making dependency through tags is used. @@ -433,7 +433,7 @@ def _depend_on_lambda_permissions_using_tag( if properties is None: properties = {} bucket["Properties"] = properties - tags = properties.get("Tags", None) + tags = properties.get("Tags") if tags is None: tags = [] properties["Tags"] = tags @@ -476,7 +476,7 @@ def _inject_notification_configuration(self, function, bucket, bucket_id): # ty sam_expect(notification_config, bucket_id, "NotificationConfiguration").to_be_a_map() - lambda_notifications = notification_config.get("LambdaConfigurations", None) + lambda_notifications = notification_config.get("LambdaConfigurations") if lambda_notifications is None: lambda_notifications = [] notification_config["LambdaConfigurations"] = lambda_notifications @@ -505,11 +505,11 @@ class SNS(PushEventSource): } Topic: str - Region: Optional[str] - FilterPolicy: Optional[Dict[str, Any]] - FilterPolicyScope: Optional[str] - SqsSubscription: Optional[Any] - RedrivePolicy: Optional[Dict[str, Any]] + Region: str | None + FilterPolicy: dict[str, Any] | None + FilterPolicyScope: str | None + SqsSubscription: Any | None + RedrivePolicy: dict[str, Any] | None @cw_timer(prefix=FUNCTION_EVETSOURCE_METRIC_PREFIX) def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] @@ -573,7 +573,7 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # SNS -> SQS(Existing) -> Lambda resources = [] - sqs_subscription: Dict[str, Any] = sam_expect( + sqs_subscription: dict[str, Any] = sam_expect( self.SqsSubscription, self.relative_id, "SqsSubscription", is_sam_event=True ).to_be_a_map() queue_arn = sqs_subscription.get("QueueArn") @@ -607,8 +607,8 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] def _check_fifo_topic( self, - topic_id: Optional[str], - template: Optional[Dict[str, Any]], + topic_id: str | None, + template: dict[str, Any] | None, intrinsics_resolver: IntrinsicsResolver, ) -> bool: if not topic_id or not template: @@ -623,10 +623,10 @@ def _inject_subscription( # noqa: PLR0913 protocol: str, endpoint: str, topic: str, - region: Optional[str], - filterPolicy: Optional[Dict[str, Any]], - filterPolicyScope: Optional[str], - redrivePolicy: Optional[Dict[str, Any]], + region: str | None, + filterPolicy: dict[str, Any] | None, + filterPolicyScope: str | None, + redrivePolicy: dict[str, Any] | None, function: Any, ) -> SNSSubscription: subscription = SNSSubscription(self.logical_id, attributes=function.get_passthrough_resource_attributes()) @@ -695,14 +695,14 @@ class Api(PushEventSource): Path: str Method: str RestApiId: str - Stage: Optional[str] - Auth: Optional[Dict[str, Any]] - RequestModel: Optional[Dict[str, Any]] - RequestParameters: Optional[List[Any]] - TimeoutInMillis: Optional[PassThrough] - ResponseTransferMode: Optional[PassThrough] - - def resources_to_link(self, resources: Dict[str, Any]) -> Dict[str, Any]: + Stage: str | None + Auth: dict[str, Any] | None + RequestModel: dict[str, Any] | None + RequestParameters: list[Any] | None + TimeoutInMillis: PassThrough | None + ResponseTransferMode: PassThrough | None + + def resources_to_link(self, resources: dict[str, Any]) -> dict[str, Any]: """ If this API Event Source refers to an explicit API resource, resolve the reference and grab necessary data from the explicit API @@ -711,8 +711,8 @@ def resources_to_link(self, resources: Dict[str, Any]) -> Dict[str, Any]: @staticmethod def resources_to_link_for_rest_api( - resources: Dict[str, Any], relative_id: str, raw_rest_api_id: Optional[Any] - ) -> Dict[str, Any]: + resources: dict[str, Any], relative_id: str, raw_rest_api_id: Any | None + ) -> dict[str, Any]: # If RestApiId is a resource in the same template, then we try find the StageName by following the reference # Otherwise we default to a wildcard. This stage name is solely used to construct the permission to # allow this stage to invoke the Lambda function. If we are unable to resolve the stage name, we will @@ -1031,9 +1031,9 @@ def _add_swagger_integration( # type: ignore[no-untyped-def] # noqa: PLR0912, P def _get_merged_definitions( self, api_id: str, - source_definition_body: Dict[str, Any], + source_definition_body: dict[str, Any], editor: SwaggerEditor, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """ Merge SAM generated swagger definition(dest_definition_body) into inline DefinitionBody(source_definition_body): - for a conflicting key, use SAM generated value @@ -1075,8 +1075,8 @@ def get_rest_api_id_string(rest_api_id: Any) -> Any: @staticmethod def add_auth_to_swagger( # noqa: PLR0912, PLR0913 - event_auth: Dict[str, Any], - api: Dict[str, Any], + event_auth: dict[str, Any], + api: dict[str, Any], api_id: str, event_id: str, method: str, @@ -1158,7 +1158,7 @@ class AlexaSkill(PushEventSource): property_types = {"SkillId": PropertyType(False, IS_STR)} - SkillId: Optional[PassThrough] + SkillId: PassThrough | None @cw_timer(prefix=FUNCTION_EVETSOURCE_METRIC_PREFIX) def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] @@ -1180,7 +1180,7 @@ class IoTRule(PushEventSource): property_types = {"Sql": PropertyType(True, IS_STR), "AwsIotSqlVersion": PropertyType(False, IS_STR)} Sql: PassThrough - AwsIotSqlVersion: Optional[PassThrough] + AwsIotSqlVersion: PassThrough | None @cw_timer(prefix=FUNCTION_EVETSOURCE_METRIC_PREFIX) def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] @@ -1232,7 +1232,7 @@ class Cognito(PushEventSource): } UserPool: Any - Trigger: Union[str, List[str]] + Trigger: Union[str, list[str]] def resources_to_link(self, resources): # type: ignore[no-untyped-def] if isinstance(self.UserPool, dict) and "Ref" in self.UserPool: @@ -1277,7 +1277,7 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] resources.append(CognitoUserPool.from_dict(userpool_id, userpool, userpool_id)) return resources - def _inject_lambda_config(self, function: Any, userpool: Dict[str, Any], userpool_id: str) -> None: + def _inject_lambda_config(self, function: Any, userpool: dict[str, Any], userpool_id: str) -> None: event_triggers = self.Trigger if isinstance(self.Trigger, str): event_triggers = [self.Trigger] @@ -1320,14 +1320,14 @@ class HttpApi(PushEventSource): "PayloadFormatVersion": PropertyType(False, IS_STR), } - Path: Optional[str] - Method: Optional[str] - ApiId: Optional[Union[str, Dict[str, str]]] - Stage: Optional[PassThrough] - Auth: Optional[PassThrough] - TimeoutInMillis: Optional[PassThrough] - RouteSettings: Optional[PassThrough] - PayloadFormatVersion: Optional[PassThrough] + Path: str | None + Method: str | None + ApiId: Union[str, dict[str, str]] | None + Stage: PassThrough | None + Auth: PassThrough | None + TimeoutInMillis: PassThrough | None + RouteSettings: PassThrough | None + PayloadFormatVersion: PassThrough | None @property def _method(self) -> str: @@ -1493,7 +1493,7 @@ def _add_openapi_integration(self, api, api_id, function, manage_swagger=False): api["DefinitionBody"] = editor.openapi def _add_auth_to_openapi_integration( - self, api: Dict[str, Any], api_id: str, editor: OpenApiEditor, auth: Dict[str, Any] + self, api: dict[str, Any], api_id: str, editor: OpenApiEditor, auth: dict[str, Any] ) -> None: """Adds authorization to the lambda integration :param api: api object diff --git a/samtranslator/model/eventsources/scheduler.py b/samtranslator/model/eventsources/scheduler.py index ff5d72ece4..220006bc40 100644 --- a/samtranslator/model/eventsources/scheduler.py +++ b/samtranslator/model/eventsources/scheduler.py @@ -1,5 +1,5 @@ from enum import Enum, auto -from typing import Any, Dict, List, Optional, Tuple, Union, cast +from typing import Any, Union, cast from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model import Property, PropertyType, Resource, ResourceMacro @@ -54,29 +54,29 @@ class SchedulerEventSource(ResourceMacro): # Below are type hints, must maintain consistent with properties_types # - pass-through to generated IAM role - PermissionsBoundary: Optional[str] + PermissionsBoundary: str | None # - pass-through to AWS::Scheduler::Schedule ScheduleExpression: str - FlexibleTimeWindow: Optional[Dict[str, Any]] - Name: Optional[PassThrough] - State: Optional[PassThrough] - Description: Optional[PassThrough] - StartDate: Optional[PassThrough] - EndDate: Optional[PassThrough] - ScheduleExpressionTimezone: Optional[PassThrough] - GroupName: Optional[PassThrough] - KmsKeyArn: Optional[PassThrough] + FlexibleTimeWindow: dict[str, Any] | None + Name: PassThrough | None + State: PassThrough | None + Description: PassThrough | None + StartDate: PassThrough | None + EndDate: PassThrough | None + ScheduleExpressionTimezone: PassThrough | None + GroupName: PassThrough | None + KmsKeyArn: PassThrough | None # - pass-through to AWS::Scheduler::Schedule's Target - Input: Optional[PassThrough] - RoleArn: Optional[PassThrough] - DeadLetterConfig: Optional[Dict[str, Any]] - RetryPolicy: Optional[PassThrough] - OmitName: Optional[bool] + Input: PassThrough | None + RoleArn: PassThrough | None + DeadLetterConfig: dict[str, Any] | None + RetryPolicy: PassThrough | None + OmitName: bool | None DEFAULT_FLEXIBLE_TIME_WINDOW = {"Mode": "OFF"} @cw_timer(prefix=FUNCTION_EVETSOURCE_METRIC_PREFIX) - def to_cloudformation(self, **kwargs: Dict[str, Any]) -> List[Resource]: + def to_cloudformation(self, **kwargs: dict[str, Any]) -> list[Resource]: """Returns the Scheduler Schedule and an IAM role. :param dict kwargs: no existing resources need to be modified @@ -102,12 +102,12 @@ def to_cloudformation(self, **kwargs: Dict[str, Any]) -> List[Resource]: passthrough_resource_attributes = target.get_passthrough_resource_attributes() - resources: List[Resource] = [] + resources: list[Resource] = [] scheduler_schedule = self._construct_scheduler_schedule_without_target(passthrough_resource_attributes) resources.append(scheduler_schedule) - dlq_queue_arn: Optional[str] = None + dlq_queue_arn: str | None = None if self.DeadLetterConfig is not None: # The dql config spec is the same as normal "Schedule" event, # so continue to use EventBridgeRuleUtils for validation. @@ -119,7 +119,7 @@ def to_cloudformation(self, **kwargs: Dict[str, Any]) -> List[Resource]: ) resources.extend(dlq_resources) - execution_role_arn: Union[str, Dict[str, Any]] = self.RoleArn # type: ignore[assignment] + execution_role_arn: Union[str, dict[str, Any]] = self.RoleArn # type: ignore[assignment] if not execution_role_arn: execution_role = self._construct_execution_role( target, target_type, passthrough_resource_attributes, dlq_queue_arn, self.PermissionsBoundary @@ -132,7 +132,7 @@ def to_cloudformation(self, **kwargs: Dict[str, Any]) -> List[Resource]: return resources def _construct_scheduler_schedule_without_target( - self, passthrough_resource_attributes: Dict[str, Any] + self, passthrough_resource_attributes: dict[str, Any] ) -> SchedulerSchedule: scheduler_schedule = SchedulerSchedule(self.logical_id, attributes=passthrough_resource_attributes) scheduler_schedule.ScheduleExpression = self.ScheduleExpression @@ -167,9 +167,9 @@ def _construct_execution_role( self, target: Resource, target_type: _SchedulerScheduleTargetType, - passthrough_resource_attributes: Dict[str, Any], - dlq_queue_arn: Optional[str], - permissions_boundary: Optional[str], + passthrough_resource_attributes: dict[str, Any], + dlq_queue_arn: str | None, + permissions_boundary: str | None, ) -> IAMRole: """Constructs the execution role for Scheduler Schedule.""" if target_type == _SchedulerScheduleTargetType.FUNCTION: @@ -195,8 +195,8 @@ def _construct_execution_role( return execution_role def _construct_scheduler_schedule_target( - self, target: Resource, execution_role_arn: Union[str, Dict[str, Any]], dead_letter_queue_arn: Optional[Any] - ) -> Dict[str, Any]: + self, target: Resource, execution_role_arn: Union[str, dict[str, Any]], dead_letter_queue_arn: Any | None + ) -> dict[str, Any]: """Constructs the Target property for the Scheduler Schedule. :returns: the Target property @@ -204,7 +204,7 @@ def _construct_scheduler_schedule_target( Inspired by https://github.com/aws/serverless-application-model/blob/a25933379e1cad3d0df4b35729ee2ec335402fdf/samtranslator/model/eventsources/push.py#L157 """ - target_dict: Dict[str, Any] = { + target_dict: dict[str, Any] = { "Arn": target.get_runtime_attr("arn"), "RoleArn": execution_role_arn, } @@ -220,8 +220,8 @@ def _construct_scheduler_schedule_target( return target_dict def _get_dlq_queue_arn_and_resources( - self, dlq_config: Dict[str, Any], passthrough_resource_attributes: Optional[Dict[str, Any]] - ) -> Tuple[Any, List[Resource]]: + self, dlq_config: dict[str, Any], passthrough_resource_attributes: dict[str, Any] | None + ) -> tuple[Any, list[Resource]]: """ Returns dlq queue arn and dlq_resources, assuming self.DeadLetterConfig has been validated. @@ -236,7 +236,7 @@ def _get_dlq_queue_arn_and_resources( self.logical_id, "QueueLogicalId must be a string", ) - dlq_resources: List[Resource] = [] + dlq_resources: list[Resource] = [] queue = SQSQueue(queue_logical_id or self.logical_id + "Queue", attributes=passthrough_resource_attributes) dlq_resources.append(queue) diff --git a/samtranslator/model/exceptions.py b/samtranslator/model/exceptions.py index a14a47f507..ad0603f382 100644 --- a/samtranslator/model/exceptions.py +++ b/samtranslator/model/exceptions.py @@ -1,7 +1,8 @@ from abc import ABC, abstractmethod from collections import defaultdict +from collections.abc import Sequence from enum import Enum -from typing import Any, Dict, List, Optional, Sequence, Union +from typing import Any, Union class ExpectedType(Enum): @@ -19,7 +20,7 @@ def message(self) -> str: """Return the exception message.""" @property - def metadata(self) -> Optional[Dict[str, Any]]: + def metadata(self) -> dict[str, Any] | None: """Return the exception metadata.""" @@ -43,7 +44,7 @@ def message(self) -> str: return f"Invalid Serverless Application Specification document. Number of errors found: {len(self.causes)}." @property - def metadata(self) -> Dict[str, List[Any]]: + def metadata(self) -> dict[str, list[Any]]: # Merge metadata in each exception to one single metadata dictionary metadata_dict = defaultdict(list) for cause in self.causes: @@ -100,9 +101,7 @@ class InvalidResourceException(ExceptionWithMessage): message -- explanation of the error """ - def __init__( - self, logical_id: Union[str, List[str]], message: str, metadata: Optional[Dict[str, Any]] = None - ) -> None: + def __init__(self, logical_id: Union[str, list[str]], message: str, metadata: dict[str, Any] | None = None) -> None: self._logical_id = logical_id self._message = message self._metadata = metadata @@ -115,7 +114,7 @@ def message(self) -> str: return f"Resource with id [{self._logical_id}] is invalid. {self._message}" @property - def metadata(self) -> Optional[Dict[str, Any]]: + def metadata(self) -> dict[str, Any] | None: return self._metadata @@ -124,8 +123,8 @@ def __init__( self, logical_id: str, key_path: str, - expected_type: Optional[ExpectedType], - message: Optional[str] = None, + expected_type: ExpectedType | None, + message: str | None = None, ) -> None: message = message or self._default_message(key_path, expected_type) super().__init__(logical_id, message) @@ -139,7 +138,7 @@ def __repr__(self) -> str: return self.message @staticmethod - def _default_message(key_path: str, expected_type: Optional[ExpectedType]) -> str: + def _default_message(key_path: str, expected_type: ExpectedType | None) -> str: if expected_type: type_description, _ = expected_type.value return f"Property '{key_path}' should be a {type_description}." @@ -151,14 +150,14 @@ def __init__( self, logical_id: str, key_path: str, - expected_type: Optional[ExpectedType], - message: Optional[str] = None, + expected_type: ExpectedType | None, + message: str | None = None, ) -> None: message = message or self._default_message(logical_id, key_path, expected_type) super().__init__(logical_id, message) @staticmethod - def _default_message(logical_id: str, key_path: str, expected_type: Optional[ExpectedType]) -> str: + def _default_message(logical_id: str, key_path: str, expected_type: ExpectedType | None) -> str: if expected_type: type_description, _ = expected_type.value return f"Attribute '{key_path}' should be a {type_description}." @@ -175,7 +174,7 @@ class InvalidEventException(ExceptionWithMessage): # Note: event_id should not be None, but currently there are too many # usage of this class with `event_id` being Optional. # TODO: refactor the code to make type correct. - def __init__(self, event_id: Optional[str], message: str) -> None: + def __init__(self, event_id: str | None, message: str) -> None: self._event_id = event_id self._message = message @@ -194,5 +193,5 @@ def prepend(exception, message, end=": "): # type: ignore[no-untyped-def] :returns: the exception """ exception.args = exception.args or ("",) - exception.args = (message + end + exception.args[0],) + exception.args[1:] + exception.args = (message + end + exception.args[0], *exception.args[1:]) return exception diff --git a/samtranslator/model/iam.py b/samtranslator/model/iam.py index 62681a97a3..8506e7b327 100644 --- a/samtranslator/model/iam.py +++ b/samtranslator/model/iam.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from samtranslator.model import GeneratedProperty, Resource from samtranslator.model.intrinsics import fnGetAtt, ref @@ -43,7 +43,7 @@ class IAMManagedPolicy(Resource): class IAMRolePolicies: @classmethod - def construct_assume_role_policy_for_service_principal(cls, service_principal: str) -> Dict[str, Any]: + def construct_assume_role_policy_for_service_principal(cls, service_principal: str) -> dict[str, Any]: return { "Version": "2012-10-17", "Statement": [ @@ -65,7 +65,7 @@ def step_functions_start_execution_role_policy(cls, state_machine_arn, logical_i } @classmethod - def stepfunctions_assume_role_policy(cls) -> Dict[str, Any]: + def stepfunctions_assume_role_policy(cls) -> dict[str, Any]: return { "Version": "2012-10-17", "Statement": [ @@ -78,7 +78,7 @@ def stepfunctions_assume_role_policy(cls) -> Dict[str, Any]: } @classmethod - def cloud_watch_log_assume_role_policy(cls) -> Dict[str, Any]: + def cloud_watch_log_assume_role_policy(cls) -> dict[str, Any]: return { "Version": "2012-10-17", "Statement": [ @@ -91,7 +91,7 @@ def cloud_watch_log_assume_role_policy(cls) -> Dict[str, Any]: } @classmethod - def scheduler_assume_role_policy(cls) -> Dict[str, Any]: + def scheduler_assume_role_policy(cls) -> dict[str, Any]: return { "Version": "2012-10-17", "Statement": [ @@ -100,7 +100,7 @@ def scheduler_assume_role_policy(cls) -> Dict[str, Any]: } @classmethod - def lambda_assume_role_policy(cls) -> Dict[str, Any]: + def lambda_assume_role_policy(cls) -> dict[str, Any]: return { "Version": "2012-10-17", "Statement": [ @@ -109,10 +109,10 @@ def lambda_assume_role_policy(cls) -> Dict[str, Any]: } @classmethod - def dead_letter_queue_policy(cls, action: Any, resource: Any) -> Dict[str, Any]: + def dead_letter_queue_policy(cls, action: Any, resource: Any) -> dict[str, Any]: """Return the DeadLetterQueue Policy to be added to the LambdaRole :returns: Policy for the DeadLetterQueue - :rtype: Dict + :rtype: dict """ return { "PolicyName": "DeadLetterQueuePolicy", @@ -123,21 +123,21 @@ def dead_letter_queue_policy(cls, action: Any, resource: Any) -> Dict[str, Any]: } @classmethod - def sqs_send_message_role_policy(cls, queue_arn: Any, logical_id: str) -> Dict[str, Any]: + def sqs_send_message_role_policy(cls, queue_arn: Any, logical_id: str) -> dict[str, Any]: return { "PolicyName": logical_id + "SQSPolicy", "PolicyDocument": {"Statement": [{"Action": "sqs:SendMessage", "Effect": "Allow", "Resource": queue_arn}]}, } @classmethod - def sns_publish_role_policy(cls, topic_arn: Any, logical_id: str) -> Dict[str, Any]: + def sns_publish_role_policy(cls, topic_arn: Any, logical_id: str) -> dict[str, Any]: return { "PolicyName": logical_id + "SNSPolicy", "PolicyDocument": {"Statement": [{"Action": "sns:publish", "Effect": "Allow", "Resource": topic_arn}]}, } @classmethod - def s3_send_event_payload_role_policy(cls, s3_arn: Any, logical_id: str) -> Dict[str, Any]: + def s3_send_event_payload_role_policy(cls, s3_arn: Any, logical_id: str) -> dict[str, Any]: s3_arn_with_wild_card = {"Fn::Join": ["/", [s3_arn, "*"]]} return { "PolicyName": logical_id + "S3Policy", @@ -150,7 +150,7 @@ def s3_send_event_payload_role_policy(cls, s3_arn: Any, logical_id: str) -> Dict } @classmethod - def event_bus_put_events_role_policy(cls, event_bus_arn: Any, logical_id: str) -> Dict[str, Any]: + def event_bus_put_events_role_policy(cls, event_bus_arn: Any, logical_id: str) -> dict[str, Any]: return { "PolicyName": logical_id + "EventBridgePolicy", "PolicyDocument": { @@ -159,7 +159,7 @@ def event_bus_put_events_role_policy(cls, event_bus_arn: Any, logical_id: str) - } @classmethod - def lambda_invoke_function_role_policy(cls, function_arn: Any, logical_id: str) -> Dict[str, Any]: + def lambda_invoke_function_role_policy(cls, function_arn: Any, logical_id: str) -> dict[str, Any]: return { "PolicyName": logical_id + "LambdaPolicy", "PolicyDocument": { diff --git a/samtranslator/model/intrinsics.py b/samtranslator/model/intrinsics.py index db955994cb..4512ec28c4 100644 --- a/samtranslator/model/intrinsics.py +++ b/samtranslator/model/intrinsics.py @@ -1,47 +1,48 @@ -from typing import Any, Dict, Iterable, List, Optional, Union +from collections.abc import Iterable +from typing import Any, Union MIN_NUM_CONDITIONS_TO_COMBINE = 2 _NUM_ARGUMENTS_REQUIRED_IN_IF = 3 _NUM_ARGUMENTS_REQUIRED_IN_GETATT = 2 -def fnGetAtt(logical_name: str, attribute_name: str) -> Dict[str, List[str]]: +def fnGetAtt(logical_name: str, attribute_name: str) -> dict[str, list[str]]: return {"Fn::GetAtt": [logical_name, attribute_name]} -def ref(logical_name: str) -> Dict[str, str]: +def ref(logical_name: str) -> dict[str, str]: return {"Ref": logical_name} -def fnJoin(delimiter: str, values: List[str]) -> Dict[str, List[Any]]: +def fnJoin(delimiter: str, values: list[str]) -> dict[str, list[Any]]: return {"Fn::Join": [delimiter, values]} -def fnSub(string: str, variables: Optional[Dict[str, Any]] = None) -> Dict[str, Union[str, List[Any]]]: +def fnSub(string: str, variables: dict[str, Any] | None = None) -> dict[str, Union[str, list[Any]]]: if variables: return {"Fn::Sub": [string, variables]} return {"Fn::Sub": string} -def fnOr(argument_list: List[Any]) -> Dict[str, List[Any]]: +def fnOr(argument_list: list[Any]) -> dict[str, list[Any]]: return {"Fn::Or": argument_list} -def fnAnd(argument_list: List[Any]) -> Dict[str, List[Any]]: +def fnAnd(argument_list: list[Any]) -> dict[str, list[Any]]: return {"Fn::And": argument_list} -def make_conditional(condition: str, true_data: Any, false_data: Optional[Any] = None) -> Dict[str, List[Any]]: +def make_conditional(condition: str, true_data: Any, false_data: Any | None = None) -> dict[str, list[Any]]: if false_data is None: false_data = {"Ref": "AWS::NoValue"} return {"Fn::If": [condition, true_data, false_data]} -def make_not_conditional(condition: str) -> Dict[str, List[Dict[str, str]]]: +def make_not_conditional(condition: str) -> dict[str, list[dict[str, str]]]: return {"Fn::Not": [{"Condition": condition}]} -def make_condition_or_list(conditions_list: Iterable[Any]) -> List[Dict[str, Any]]: +def make_condition_or_list(conditions_list: Iterable[Any]) -> list[dict[str, Any]]: condition_or_list = [] for condition in conditions_list: c = {"Condition": condition} @@ -49,12 +50,12 @@ def make_condition_or_list(conditions_list: Iterable[Any]) -> List[Dict[str, Any return condition_or_list -def make_or_condition(conditions_list: Iterable[Any]) -> Dict[str, List[Dict[str, Any]]]: +def make_or_condition(conditions_list: Iterable[Any]) -> dict[str, list[dict[str, Any]]]: or_list = make_condition_or_list(conditions_list) return fnOr(or_list) -def make_and_condition(conditions_list: Iterable[Any]) -> Dict[str, List[Dict[str, Any]]]: +def make_and_condition(conditions_list: Iterable[Any]) -> dict[str, list[dict[str, Any]]]: and_list = make_condition_or_list(conditions_list) return fnAnd(and_list) @@ -78,8 +79,8 @@ def calculate_number_of_conditions(conditions_length: int, max_conditions: int) def make_combined_condition( - conditions_list: List[str], condition_name: str -) -> Optional[Dict[str, Dict[str, List[Dict[str, Any]]]]]: + conditions_list: list[str], condition_name: str +) -> dict[str, dict[str, list[dict[str, Any]]]] | None: """ Makes a combined condition using Fn::Or. Since Fn::Or only accepts up to 10 conditions, this method optionally creates multiple conditions. These conditions are named based on @@ -115,7 +116,7 @@ def make_combined_condition( return conditions -def make_shorthand(intrinsic_dict: Dict[str, Any]) -> str: +def make_shorthand(intrinsic_dict: dict[str, Any]) -> str: """ Converts a given intrinsics dictionary into a short-hand notation that Fn::Sub can use. Only Ref and Fn::GetAtt support shorthands. @@ -147,7 +148,7 @@ def is_intrinsic(_input: Any) -> bool: if _input is not None and isinstance(_input, dict) and len(_input) == 1: key: str = next(iter(_input.keys())) - return key == "Ref" or key == "Condition" or key.startswith("Fn::") + return key in {"Ref", "Condition"} or key.startswith("Fn::") return False @@ -202,7 +203,7 @@ def is_intrinsic_no_value(_input: Any) -> bool: return key == "Ref" and _input["Ref"] == "AWS::NoValue" -def get_logical_id_from_intrinsic(_input: Any) -> Optional[str]: +def get_logical_id_from_intrinsic(_input: Any) -> str | None: """ Verify if input is an Fn:GetAtt or Ref intrinsic diff --git a/samtranslator/model/lambda_.py b/samtranslator/model/lambda_.py index 11dfae55a9..f5e4f2cc88 100644 --- a/samtranslator/model/lambda_.py +++ b/samtranslator/model/lambda_.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Union +from typing import Any, Union from samtranslator.model import GeneratedProperty, Resource from samtranslator.model.intrinsics import fnGetAtt, ref @@ -43,37 +43,37 @@ class LambdaFunction(Resource): "DurableConfig": GeneratedProperty(), } - Code: Dict[str, Any] - PackageType: Optional[str] - DeadLetterConfig: Optional[Dict[str, Any]] - Description: Optional[Intrinsicable[str]] - FunctionName: Optional[Intrinsicable[str]] - Handler: Optional[str] - MemorySize: Optional[Intrinsicable[int]] - Role: Optional[Intrinsicable[str]] - Runtime: Optional[str] - Timeout: Optional[Intrinsicable[int]] - VpcConfig: Optional[Dict[str, Any]] - Environment: Optional[Dict[str, Any]] - Tags: Optional[List[Dict[str, Any]]] - TracingConfig: Optional[Dict[str, Any]] - KmsKeyArn: Optional[Intrinsicable[str]] - Layers: Optional[List[Any]] - ReservedConcurrentExecutions: Optional[Any] - FileSystemConfigs: Optional[Dict[str, Any]] - CodeSigningConfigArn: Optional[Intrinsicable[str]] - ImageConfig: Optional[Dict[str, Any]] - Architectures: Optional[List[Any]] - SnapStart: Optional[Dict[str, Any]] - EphemeralStorage: Optional[Dict[str, Any]] - RuntimeManagementConfig: Optional[Dict[str, Any]] - LoggingConfig: Optional[Dict[str, Any]] - RecursiveLoop: Optional[str] - CapacityProviderConfig: Optional[Dict[str, Any]] - FunctionScalingConfig: Optional[Dict[str, Any]] - PublishToLatestPublished: Optional[Dict[str, Any]] - TenancyConfig: Optional[Dict[str, Any]] - DurableConfig: Optional[Dict[str, Any]] + Code: dict[str, Any] + PackageType: str | None + DeadLetterConfig: dict[str, Any] | None + Description: Intrinsicable[str] | None + FunctionName: Intrinsicable[str] | None + Handler: str | None + MemorySize: Intrinsicable[int] | None + Role: Intrinsicable[str] | None + Runtime: str | None + Timeout: Intrinsicable[int] | None + VpcConfig: dict[str, Any] | None + Environment: dict[str, Any] | None + Tags: list[dict[str, Any]] | None + TracingConfig: dict[str, Any] | None + KmsKeyArn: Intrinsicable[str] | None + Layers: list[Any] | None + ReservedConcurrentExecutions: Any | None + FileSystemConfigs: dict[str, Any] | None + CodeSigningConfigArn: Intrinsicable[str] | None + ImageConfig: dict[str, Any] | None + Architectures: list[Any] | None + SnapStart: dict[str, Any] | None + EphemeralStorage: dict[str, Any] | None + RuntimeManagementConfig: dict[str, Any] | None + LoggingConfig: dict[str, Any] | None + RecursiveLoop: str | None + CapacityProviderConfig: dict[str, Any] | None + FunctionScalingConfig: dict[str, Any] | None + PublishToLatestPublished: dict[str, Any] | None + TenancyConfig: dict[str, Any] | None + DurableConfig: dict[str, Any] | None runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")} @@ -179,12 +179,12 @@ class LambdaLayerVersion(Resource): "LicenseInfo": GeneratedProperty(), } - Content: Dict[str, Any] - Description: Optional[Intrinsicable[str]] - LayerName: Optional[Intrinsicable[str]] - CompatibleArchitectures: Optional[List[Union[str, Dict[str, Any]]]] - CompatibleRuntimes: Optional[List[Union[str, Dict[str, Any]]]] - LicenseInfo: Optional[Intrinsicable[str]] + Content: dict[str, Any] + Description: Intrinsicable[str] | None + LayerName: Intrinsicable[str] | None + CompatibleArchitectures: list[Union[str, dict[str, Any]]] | None + CompatibleRuntimes: list[Union[str, dict[str, Any]]] | None + LicenseInfo: Intrinsicable[str] | None runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")} diff --git a/samtranslator/model/preferences/deployment_preference_collection.py b/samtranslator/model/preferences/deployment_preference_collection.py index ead06abe9b..2387e3950e 100644 --- a/samtranslator/model/preferences/deployment_preference_collection.py +++ b/samtranslator/model/preferences/deployment_preference_collection.py @@ -1,5 +1,5 @@ import copy -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Union, cast from samtranslator.model.codedeploy import CodeDeployApplication, CodeDeployDeploymentGroup from samtranslator.model.exceptions import InvalidResourceException @@ -51,15 +51,15 @@ def __init__(self) -> None: This collection stores an internal dict of the deployment preferences for each function's deployment preference in the SAM Template. """ - self._resource_preferences: Dict[str, Any] = {} + self._resource_preferences: dict[str, Any] = {} def add( self, logical_id: str, - deployment_preference_dict: Dict[str, Any], - condition: Optional[str] = None, - tags: Optional[Dict[str, Any]] = None, - propagate_tags: Optional[bool] = False, + deployment_preference_dict: dict[str, Any], + condition: str | None = None, + tags: dict[str, Any] | None = None, + propagate_tags: bool | None = False, ) -> None: """ Add this deployment preference to the collection @@ -100,7 +100,7 @@ def can_skip_service_role(self) -> bool: """ return all(preference.role or not preference.enabled for preference in self._resource_preferences.values()) - def needs_resource_condition(self) -> Union[Dict[str, Any], bool]: + def needs_resource_condition(self) -> Union[dict[str, Any], bool]: """ If all preferences have a condition, all code deploy resources need to be conditionally created :return: True, if a condition needs to be created @@ -110,10 +110,10 @@ def needs_resource_condition(self) -> Union[Dict[str, Any], bool]: not preference.condition and preference.enabled for preference in self._resource_preferences.values() ) - def get_all_deployment_conditions(self) -> List[str]: + def get_all_deployment_conditions(self) -> list[str]: """ Returns a list of all conditions associated with the deployment preference resources - :return: List of condition names + :return: list of condition names """ conditions_set = {preference.condition for preference in self._resource_preferences.values()} if None in conditions_set: @@ -121,14 +121,14 @@ def get_all_deployment_conditions(self) -> List[str]: conditions_set.remove(None) return list(conditions_set) - def create_aggregate_deployment_condition(self) -> Union[None, Dict[str, Dict[str, List[Dict[str, Any]]]]]: + def create_aggregate_deployment_condition(self) -> Union[None, dict[str, dict[str, list[dict[str, Any]]]]]: """ Creates an aggregate deployment condition if necessary :return: None if <2 conditions are found, otherwise a dictionary of new conditions to add to template """ return make_combined_condition(self.get_all_deployment_conditions(), CODE_DEPLOY_CONDITION_NAME) - def enabled_logical_ids(self) -> List[str]: + def enabled_logical_ids(self) -> list[str]: """ :return: only the logical id's for the deployment preferences in this collection which are enabled """ @@ -138,7 +138,7 @@ def get_codedeploy_application(self) -> CodeDeployApplication: codedeploy_application_resource = CodeDeployApplication(CODEDEPLOY_APPLICATION_LOGICAL_ID) codedeploy_application_resource.ComputePlatform = "Lambda" - merged_tags: Dict[str, Any] = {} + merged_tags: dict[str, Any] = {} for preference in self._resource_preferences.values(): if preference.enabled and preference.propagate_tags and preference.tags: merged_tags.update(preference.tags) @@ -183,7 +183,7 @@ def get_codedeploy_iam_role(self) -> IAMRole: condition_name = conditions.pop() iam_role.set_resource_attribute("Condition", condition_name) - merged_tags: Dict[str, Any] = {} + merged_tags: dict[str, Any] = {} for preference in self._resource_preferences.values(): if preference.enabled and preference.propagate_tags and preference.tags: merged_tags.update(preference.tags) diff --git a/samtranslator/model/resource_policies.py b/samtranslator/model/resource_policies.py index a62a960799..ce139f3062 100644 --- a/samtranslator/model/resource_policies.py +++ b/samtranslator/model/resource_policies.py @@ -1,6 +1,6 @@ from collections import namedtuple from enum import Enum -from typing import Any, Dict, List +from typing import Any from samtranslator.model.exceptions import InvalidTemplateException from samtranslator.model.intrinsics import ( @@ -29,7 +29,7 @@ class ResourcePolicies: POLICIES_PROPERTY_NAME = "Policies" - def __init__(self, resource_properties: Dict[str, Any], policy_template_processor: Any = None) -> None: + def __init__(self, resource_properties: dict[str, Any], policy_template_processor: Any = None) -> None: """ Initialize with policies data from resource's properties @@ -56,7 +56,7 @@ def get(self): # type: ignore[no-untyped-def] def __len__(self): # type: ignore[no-untyped-def] return len(self.policies) - def _get_policies(self, resource_properties: Dict[str, Any]) -> List[Any]: + def _get_policies(self, resource_properties: dict[str, Any]) -> list[Any]: """ Returns a list of policies from the resource properties. This method knows how to interpret and handle polymorphic nature of the policies property. @@ -64,16 +64,16 @@ def _get_policies(self, resource_properties: Dict[str, Any]) -> List[Any]: Policies can be one of the following: * Managed policy name: string - * List of managed policy names: list of strings + * list of managed policy names: list of strings * IAM Policy document: dict containing Statement key - * List of IAM Policy documents: list of IAM Policy Document + * list of IAM Policy documents: list of IAM Policy Document * Policy Template: dict with only one key where key is in list of supported policy template names - * List of Policy Templates: list of Policy Template + * list of Policy Templates: list of Policy Template :param dict resource_properties: Dictionary of resource properties containing the policies property. It is assumed that this is already a dictionary and contains policies key. - :return list of PolicyEntry: List of policies, where each item is an instance of named tuple `PolicyEntry` + :return list of PolicyEntry: list of policies, where each item is an instance of named tuple `PolicyEntry` """ policies = None diff --git a/samtranslator/model/role_utils/role_constructor.py b/samtranslator/model/role_utils/role_constructor.py index 825bd55b3f..40fc1bc493 100644 --- a/samtranslator/model/role_utils/role_constructor.py +++ b/samtranslator/model/role_utils/role_constructor.py @@ -1,4 +1,5 @@ -from typing import Any, Callable, Dict, List, Optional +from collections.abc import Callable +from typing import Any from samtranslator.internal.managed_policies import get_bundled_managed_policy_map from samtranslator.internal.types import GetManagedPolicyMap @@ -11,8 +12,8 @@ def _get_managed_policy_arn( name: str, - managed_policy_map: Optional[Dict[str, str]], - get_managed_policy_map: Optional[GetManagedPolicyMap], + managed_policy_map: dict[str, str] | None, + get_managed_policy_map: GetManagedPolicyMap | None, ) -> str: """ Get the ARN of a AWS managed policy name. Used in Policies property of @@ -61,20 +62,20 @@ def _get_managed_policy_arn( def _convert_intrinsic_if_values( - intrinsic_if: Dict[str, List[Any]], is_convertible: Callable[[Any], Any], convert: Callable[[Any], Any] -) -> Dict[str, List[Any]]: + intrinsic_if: dict[str, list[Any]], is_convertible: Callable[[Any], Any], convert: Callable[[Any], Any] +) -> dict[str, list[Any]]: """ Convert the true and false value of the intrinsic if function according to `convert` function. :param intrinsic_if: A dict of the form {"Fn::If": [condition, value_if_true, value_if_false]} - :type intrinsic_if: Dict[str, List[Any]] + :type intrinsic_if: dict[str, list[Any]] :param is_convertible: The function used to decide if the value must be converted :type convert: Callable[[Any], Any] :param convert: The function used to make the conversion :type convert: Callable[[Any], Any] :return: The input dict with values converted - :rtype: Dict[str, List[Any]] + :rtype: dict[str, list[Any]] """ value_if_true = intrinsic_if["Fn::If"][1] value_if_false = intrinsic_if["Fn::If"][2] @@ -108,8 +109,8 @@ def construct_role_for_resource( # type: ignore[no-untyped-def] # noqa: PLR0913 :param managed_policy_map: Map of managed policy names to the ARNs :param assume_role_policy_document: The trust policy that must be associated with the role :param resource_policies: ResourcePolicies object encapuslating the policies property of SAM resource - :param managed_policy_arns: List of managed policy ARNs to be associated with the role - :param policy_documents: List of policy documents to be associated with the role + :param managed_policy_arns: list of managed policy ARNs to be associated with the role + :param policy_documents: list of policy documents to be associated with the role :param role_path: The path to the role :param permissions_boundary: The ARN of the policy used to set the permissions boundary for the role :param tags: Tags to be associated with the role diff --git a/samtranslator/model/route53.py b/samtranslator/model/route53.py index deab5c5221..61c80cb3fc 100644 --- a/samtranslator/model/route53.py +++ b/samtranslator/model/route53.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import Any from samtranslator.model import GeneratedProperty, Resource from samtranslator.utils.types import Intrinsicable @@ -12,6 +12,6 @@ class Route53RecordSetGroup(Resource): "RecordSets": GeneratedProperty(), } - HostedZoneId: Optional[Intrinsicable[str]] - HostedZoneName: Optional[Intrinsicable[str]] - RecordSets: Optional[List[Any]] + HostedZoneId: Intrinsicable[str] | None + HostedZoneName: Intrinsicable[str] | None + RecordSets: list[Any] | None diff --git a/samtranslator/model/s3_utils/uri_parser.py b/samtranslator/model/s3_utils/uri_parser.py index 5985bdbe1f..9f810738bc 100644 --- a/samtranslator/model/s3_utils/uri_parser.py +++ b/samtranslator/model/s3_utils/uri_parser.py @@ -1,11 +1,11 @@ from re import search -from typing import Any, Dict, Optional, Union +from typing import Any, Union from urllib.parse import parse_qs, urlparse from samtranslator.model.exceptions import InvalidResourceException -def parse_s3_uri(uri: Any) -> Optional[Dict[str, Any]]: +def parse_s3_uri(uri: Any) -> dict[str, Any] | None: """Parses a S3 Uri into a dictionary of the Bucket, Key, and VersionId :return: a BodyS3Location dict or None if not an S3 Uri @@ -65,8 +65,8 @@ def construct_image_code_object(image_uri, logical_id, property_name): # type: def construct_s3_location_object( - location_uri: Union[str, Dict[str, Any]], logical_id: str, property_name: str -) -> Dict[str, Any]: + location_uri: Union[str, dict[str, Any]], logical_id: str, property_name: str +) -> dict[str, Any]: """Constructs a Lambda `Code` or `Content` property, from the SAM `CodeUri` or `ContentUri` property. This follows the current scheme for Lambda Functions and LayerVersions. diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index d58642828f..07d6429b26 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -3,9 +3,10 @@ import copy import re import sys +from collections.abc import Callable from contextlib import suppress from types import ModuleType -from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union, cast +from typing import Any, Literal, Union, cast import samtranslator.model.eventsources import samtranslator.model.eventsources.cloudwatchlogs @@ -154,7 +155,7 @@ # Utility function to throw an error when using functionality that doesn't work in Python 3.14 (need migration to Pydantic v2) -def check_python_314_compatibility(module: Optional[ModuleType], functionality: str) -> None: +def check_python_314_compatibility(module: ModuleType | None, functionality: str) -> None: if sys.version_info >= (3, 14) and module is None: raise RuntimeError( f"{functionality} functionalities are temporarily not supported when running SAM in Python 3.14" @@ -219,54 +220,54 @@ class SamFunction(SamResourceMacro): "DurableConfig": PropertyType(False, IS_DICT), } - FunctionName: Optional[Intrinsicable[str]] - Handler: Optional[str] - Runtime: Optional[str] - CodeUri: Optional[Any] - ImageUri: Optional[str] - PackageType: Optional[str] - InlineCode: Optional[Any] - DeadLetterQueue: Optional[Dict[str, Any]] - Description: Optional[Intrinsicable[str]] - MemorySize: Optional[Intrinsicable[int]] - Timeout: Optional[Intrinsicable[int]] - VpcConfig: Optional[Dict[str, Any]] - Role: Optional[Intrinsicable[str]] - AssumeRolePolicyDocument: Optional[Dict[str, Any]] - Policies: Optional[List[Any]] - RolePath: Optional[PassThrough] - PermissionsBoundary: Optional[Intrinsicable[str]] - Environment: Optional[Dict[str, Any]] - Events: Optional[Dict[str, Any]] - Tags: Optional[Dict[str, Any]] - PropagateTags: Optional[bool] - Tracing: Optional[Intrinsicable[str]] - KmsKeyArn: Optional[Intrinsicable[str]] - DeploymentPreference: Optional[Dict[str, Any]] - ReservedConcurrentExecutions: Optional[Any] - Layers: Optional[List[Any]] - EventInvokeConfig: Optional[Dict[str, Any]] - EphemeralStorage: Optional[Dict[str, Any]] - AutoPublishAlias: Optional[Intrinsicable[str]] - AutoPublishCodeSha256: Optional[Intrinsicable[str]] - AutoPublishAliasAllProperties: Optional[bool] - VersionDescription: Optional[Intrinsicable[str]] - ProvisionedConcurrencyConfig: Optional[Dict[str, Any]] - FileSystemConfigs: Optional[Dict[str, Any]] - ImageConfig: Optional[Dict[str, Any]] - CodeSigningConfigArn: Optional[Intrinsicable[str]] - Architectures: Optional[List[Any]] - SnapStart: Optional[Dict[str, Any]] - FunctionUrlConfig: Optional[Dict[str, Any]] - LoggingConfig: Optional[Dict[str, Any]] - RecursiveLoop: Optional[str] - SourceKMSKeyArn: Optional[str] - CapacityProviderConfig: Optional[Dict[str, Any]] - FunctionScalingConfig: Optional[Dict[str, Any]] - PublishToLatestPublished: Optional[PassThrough] - VersionDeletionPolicy: Optional[Intrinsicable[str]] - TenancyConfig: Optional[Dict[str, Any]] - DurableConfig: Optional[Dict[str, Any]] + FunctionName: Intrinsicable[str] | None + Handler: str | None + Runtime: str | None + CodeUri: Any | None + ImageUri: str | None + PackageType: str | None + InlineCode: Any | None + DeadLetterQueue: dict[str, Any] | None + Description: Intrinsicable[str] | None + MemorySize: Intrinsicable[int] | None + Timeout: Intrinsicable[int] | None + VpcConfig: dict[str, Any] | None + Role: Intrinsicable[str] | None + AssumeRolePolicyDocument: dict[str, Any] | None + Policies: list[Any] | None + RolePath: PassThrough | None + PermissionsBoundary: Intrinsicable[str] | None + Environment: dict[str, Any] | None + Events: dict[str, Any] | None + Tags: dict[str, Any] | None + PropagateTags: bool | None + Tracing: Intrinsicable[str] | None + KmsKeyArn: Intrinsicable[str] | None + DeploymentPreference: dict[str, Any] | None + ReservedConcurrentExecutions: Any | None + Layers: list[Any] | None + EventInvokeConfig: dict[str, Any] | None + EphemeralStorage: dict[str, Any] | None + AutoPublishAlias: Intrinsicable[str] | None + AutoPublishCodeSha256: Intrinsicable[str] | None + AutoPublishAliasAllProperties: bool | None + VersionDescription: Intrinsicable[str] | None + ProvisionedConcurrencyConfig: dict[str, Any] | None + FileSystemConfigs: dict[str, Any] | None + ImageConfig: dict[str, Any] | None + CodeSigningConfigArn: Intrinsicable[str] | None + Architectures: list[Any] | None + SnapStart: dict[str, Any] | None + FunctionUrlConfig: dict[str, Any] | None + LoggingConfig: dict[str, Any] | None + RecursiveLoop: str | None + SourceKMSKeyArn: str | None + CapacityProviderConfig: dict[str, Any] | None + FunctionScalingConfig: dict[str, Any] | None + PublishToLatestPublished: PassThrough | None + VersionDeletionPolicy: Intrinsicable[str] | None + TenancyConfig: dict[str, Any] | None + DurableConfig: dict[str, Any] | None event_resolver = ResourceTypeResolver( samtranslator.model.eventsources, @@ -280,7 +281,7 @@ class SamFunction(SamResourceMacro): dead_letter_queue_policy_actions = {"SQS": "sqs:SendMessage", "SNS": "sns:Publish"} # Conditions - conditions: Dict[str, Any] = {} # TODO: Replace `Any` with something more specific + conditions: dict[str, Any] = {} # TODO: Replace `Any` with something more specific # Customers can refer to the following properties of SAM function referable_properties = { @@ -304,7 +305,7 @@ class SamFunction(SamResourceMacro): # (ValidationRule.CONDITIONAL_REQUIREMENT, ["ProvisionedConcurrencyConfig", "AutoPublishAlias"]), ] - def resources_to_link(self, resources: Dict[str, Any]) -> Dict[str, Any]: + def resources_to_link(self, resources: dict[str, Any]) -> dict[str, Any]: try: return {"event_resources": self._event_resources_to_link(resources)} except InvalidEventException as e: @@ -319,10 +320,10 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: P :returns: a list of vanilla CloudFormation Resources, to which this Function expands :rtype: list """ - resources: List[Any] = [] + resources: list[Any] = [] intrinsics_resolver: IntrinsicsResolver = kwargs["intrinsics_resolver"] resource_resolver: ResourceResolver = kwargs["resource_resolver"] - mappings_resolver: Optional[IntrinsicsResolver] = kwargs.get("mappings_resolver") + mappings_resolver: IntrinsicsResolver | None = kwargs.get("mappings_resolver") conditions = kwargs.get("conditions", {}) feature_toggle = kwargs.get("feature_toggle") @@ -345,7 +346,7 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: P "To set ProvisionedConcurrencyConfig AutoPublishALias must be defined on the function", ) - lambda_alias: Optional[LambdaAlias] = None + lambda_alias: LambdaAlias | None = None alias_name = "" if self.AutoPublishAlias: alias_name = self._get_resolved_alias_name("AutoPublishAlias", self.AutoPublishAlias, intrinsics_resolver) @@ -395,7 +396,7 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: P self.get_passthrough_resource_attributes(), feature_toggle, ) - event_invoke_policies: List[Dict[str, Any]] = [] + event_invoke_policies: list[dict[str, Any]] = [] if self.EventInvokeConfig: function_name = lambda_function.logical_id event_invoke_resources, event_invoke_policies = self._construct_event_invoke_config( @@ -447,14 +448,14 @@ def _make_lambda_role( lambda_function: LambdaFunction, intrinsics_resolver: IntrinsicsResolver, execution_role: IAMRole, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """ Analyzes lambda role requirements and returns the changes needed. Returns: - Dict containing: + dict containing: - 'lambda_role_value': Any - value to set for lambda_function.Role - - 'new_condition': Dict|None - new condition to add to conditions dict + - 'new_condition': dict|None - new condition to add to conditions dict - 'iam_role_resource' : IAMRole - IAM Role used for Lambda execution """ lambda_role = lambda_function.Role @@ -490,15 +491,15 @@ def _make_lambda_role( "iam_role_resource": execution_role, } - def _construct_event_invoke_config( # noqa: PLR0913 + def _construct_event_invoke_config( self, function_name: str, alias_name: str, - lambda_alias: Optional[LambdaAlias], + lambda_alias: LambdaAlias | None, intrinsics_resolver: IntrinsicsResolver, conditions: Any, - event_invoke_config: Dict[str, Any], - ) -> Tuple[List[Any], List[Dict[str, Any]]]: + event_invoke_config: dict[str, Any], + ) -> tuple[list[Any], list[dict[str, Any]]]: """ Create a `AWS::Lambda::EventInvokeConfig` based on the input dict `EventInvokeConfig` """ @@ -553,8 +554,8 @@ def _construct_event_invoke_config( # noqa: PLR0913 return resources, policy_document def _validate_and_inject_resource( - self, dest_config: Dict[str, Any], event: str, logical_id: str, conditions: Dict[str, Any] - ) -> Tuple[Optional[Resource], Optional[Any], Dict[str, Any]]: + self, dest_config: dict[str, Any], event: str, logical_id: str, conditions: dict[str, Any] + ) -> tuple[Resource | None, Any | None, dict[str, Any]]: """ For Event Invoke Config, if the user has not specified a destination ARN for SQS/SNS, SAM auto creates a SQS and SNS resource with defaults. Intrinsics are supported in the Destination @@ -563,7 +564,7 @@ def _validate_and_inject_resource( """ accepted_types_list = ["SQS", "SNS", "EventBridge", "Lambda", "S3Bucket"] auto_inject_list = ["SQS", "SNS"] - resource: Optional[Union[SNSTopic, SQSQueue]] = None + resource: Union[SNSTopic, SQSQueue] | None = None policy = {} destination = dest_config.get("Destination") @@ -609,7 +610,7 @@ def _validate_and_inject_resource( return resource, destination, policy - def _make_and_conditions(self, resource_condition: Any, property_condition: Any, conditions: Dict[str, Any]) -> Any: + def _make_and_conditions(self, resource_condition: Any, property_condition: Any, conditions: dict[str, Any]) -> Any: if resource_condition is None: return property_condition @@ -622,7 +623,7 @@ def _make_and_conditions(self, resource_condition: Any, property_condition: Any, return condition_name - def _get_or_make_condition(self, destination: Any, logical_id: str, conditions: Dict[str, Any]) -> Tuple[Any, Any]: + def _get_or_make_condition(self, destination: Any, logical_id: str, conditions: dict[str, Any]) -> tuple[Any, Any]: """ This method checks if there is an If condition on Destination property. Since we auto create SQS and SNS if the destination ARN is not provided, we need to make sure that If condition @@ -784,7 +785,7 @@ def _construct_lambda_function(self, intrinsics_resolver: IntrinsicsResolver) -> self._validate_package_type(lambda_function) return lambda_function - def _transform_capacity_provider_config(self) -> Dict[str, Any]: + def _transform_capacity_provider_config(self) -> dict[str, Any]: """ Transform SAM CapacityProviderConfig to CloudFormation format. @@ -812,12 +813,12 @@ def _transform_capacity_provider_config(self) -> Dict[str, Any]: raise InvalidResourceException(self.logical_id, f"Invalid CapacityProviderConfig: {e!s}") from e # Extract validated properties - cast to Any to handle SamIntrinsicable types - capacity_provider_arn: Optional[SamIntrinsicable[str]] = validated_model.Arn - max_concurrency: Optional[SamIntrinsicable[int]] = validated_model.PerExecutionEnvironmentMaxConcurrency - memory_to_vcpu_ratio: Optional[SamIntrinsicable[float]] = validated_model.ExecutionEnvironmentMemoryGiBPerVCpu + capacity_provider_arn: SamIntrinsicable[str] | None = validated_model.Arn + max_concurrency: SamIntrinsicable[int] | None = validated_model.PerExecutionEnvironmentMaxConcurrency + memory_to_vcpu_ratio: SamIntrinsicable[float] | None = validated_model.ExecutionEnvironmentMemoryGiBPerVCpu # Build the transformed structure - ec2_config: Dict[str, Any] = {"CapacityProviderArn": capacity_provider_arn} + ec2_config: dict[str, Any] = {"CapacityProviderArn": capacity_provider_arn} if max_concurrency is not None: ec2_config["PerExecutionEnvironmentMaxConcurrency"] = max_concurrency @@ -828,8 +829,8 @@ def _transform_capacity_provider_config(self) -> Dict[str, Any]: return {"LambdaManagedInstancesCapacityProviderConfig": ec2_config} def _add_event_invoke_managed_policy( - self, dest_config: Dict[str, Any], logical_id: str, dest_arn: Any - ) -> Dict[str, Any]: + self, dest_config: dict[str, Any], logical_id: str, dest_arn: Any + ) -> dict[str, Any]: if dest_config and dest_config.get("Type"): _type = dest_config.get("Type") if _type == "SQS": @@ -847,10 +848,10 @@ def _add_event_invoke_managed_policy( def _construct_role( self, - managed_policy_map: Dict[str, Any], - event_invoke_policies: List[Dict[str, Any]], + managed_policy_map: dict[str, Any], + event_invoke_policies: list[dict[str, Any]], intrinsics_resolver: IntrinsicsResolver, - get_managed_policy_map: Optional[GetManagedPolicyMap] = None, + get_managed_policy_map: GetManagedPolicyMap | None = None, ) -> IAMRole: """Constructs a Lambda execution role based on this SAM function's Policies property. @@ -956,7 +957,7 @@ def _validate_package_type_image() -> None: # Call appropriate validation function based on the package type. return _validate_per_package_type[packagetype]() - def _validate_dlq(self, dead_letter_queue: Dict[str, Any]) -> None: + def _validate_dlq(self, dead_letter_queue: dict[str, Any]) -> None: """Validates whether the DeadLetterQueue LogicalId is validation :raise: InvalidResourceException """ @@ -975,7 +976,7 @@ def _validate_dlq(self, dead_letter_queue: Dict[str, Any]) -> None: if dlq_type not in self.dead_letter_queue_policy_actions: raise InvalidResourceException(self.logical_id, f"'DeadLetterQueue' requires Type of {valid_dlq_types}") - def _event_resources_to_link(self, resources: Dict[str, Any]) -> Dict[str, Any]: + def _event_resources_to_link(self, resources: dict[str, Any]) -> dict[str, Any]: event_resources = {} if self.Events: for logical_id, event_dict in self.Events.items(): @@ -989,7 +990,7 @@ def _event_resources_to_link(self, resources: Dict[str, Any]) -> Dict[str, Any]: return event_resources @staticmethod - def order_events(event: Tuple[str, Any]) -> Any: + def order_events(event: tuple[str, Any]) -> Any: """ Helper method for sorting Function Events. Returns a key to use in sorting this event @@ -1003,15 +1004,15 @@ def order_events(event: Tuple[str, Any]) -> Any: return logical_id return event_dict.get("Properties", {}).get("Path", logical_id) - def _generate_event_resources( # noqa: PLR0913 + def _generate_event_resources( self, lambda_function: LambdaFunction, - execution_role: Optional[IAMRole], + execution_role: IAMRole | None, event_resources: Any, intrinsics_resolver: IntrinsicsResolver, - lambda_alias: Optional[LambdaAlias] = None, - original_template: Optional[Dict[str, Any]] = None, - ) -> List[Any]: + lambda_alias: LambdaAlias | None = None, + original_template: dict[str, Any] | None = None, + ) -> list[Any]: """Generates and returns the resources associated with this function's events. :param model.lambda_.LambdaFunction lambda_function: generated Lambda function @@ -1049,7 +1050,7 @@ def _generate_event_resources( # noqa: PLR0913 return resources - def _construct_code_dict(self) -> Dict[str, Any]: + def _construct_code_dict(self) -> dict[str, Any]: """Constructs Lambda Code Dictionary based on the accepted SAM artifact properties such as `InlineCode`, `CodeUri` and `ImageUri` and also raises errors if more than one of them is defined. `PackageType` determines which artifacts are considered. @@ -1070,11 +1071,11 @@ def _construct_code_dict(self) -> Dict[str, Any]: # Inline function for transformation of inline code. # It accepts arbitrary argumemnts, because the arguments do not matter for the result. - def _construct_inline_code(*args: Any, **kwargs: Dict[str, Any]) -> Dict[str, Any]: + def _construct_inline_code(*args: Any, **kwargs: dict[str, Any]) -> dict[str, Any]: return {"ZipFile": self.InlineCode} # dispatch mechanism per artifact on how it needs to be transformed. - artifact_dispatch: Dict[str, Callable[..., Dict[str, Any]]] = { + artifact_dispatch: dict[str, Callable[..., dict[str, Any]]] = { "InlineCode": _construct_inline_code, "CodeUri": construct_s3_location_object, "ImageUri": construct_image_code_object, @@ -1101,7 +1102,7 @@ def _construct_inline_code(*args: Any, **kwargs: Dict[str, Any]) -> Dict[str, An filtered_key = "ImageUri" else: raise InvalidResourceException(self.logical_id, "Either 'InlineCode' or 'CodeUri' must be set.") - dispatch_function: Callable[..., Dict[str, Any]] = artifact_dispatch[filtered_key] + dispatch_function: Callable[..., dict[str, Any]] = artifact_dispatch[filtered_key] code_dict = dispatch_function(artifacts[filtered_key], self.logical_id, filtered_key) if self.SourceKMSKeyArn and packagetype == ZIP: code_dict["SourceKMSKeyArn"] = self.SourceKMSKeyArn @@ -1126,7 +1127,7 @@ def _construct_version( # noqa: PLR0912 function: LambdaFunction, intrinsics_resolver: IntrinsicsResolver, resource_resolver: ResourceResolver, - code_sha256: Optional[str] = None, + code_sha256: str | None = None, ) -> LambdaVersion: """Constructs a Lambda Version resource that will be auto-published when CodeUri of the function changes. Old versions will not be deleted without a direct reference from the CloudFormation template. @@ -1261,14 +1262,14 @@ def _construct_alias(self, name: str, function: LambdaFunction, version: LambdaV return alias - def _validate_deployment_preference_and_add_update_policy( # noqa: PLR0913 + def _validate_deployment_preference_and_add_update_policy( self, - deployment_preference_collection: Optional[DeploymentPreferenceCollection], - lambda_alias: Optional[LambdaAlias], + deployment_preference_collection: DeploymentPreferenceCollection | None, + lambda_alias: LambdaAlias | None, intrinsics_resolver: IntrinsicsResolver, mappings_resolver: IntrinsicsResolver, - passthrough_resource_attributes: Dict[str, Any], - feature_toggle: Optional[FeatureToggle] = None, + passthrough_resource_attributes: dict[str, Any], + feature_toggle: FeatureToggle | None = None, ) -> None: if not self.DeploymentPreference: return @@ -1326,7 +1327,7 @@ def _validate_deployment_preference_and_add_update_policy( # noqa: PLR0913 def _resolve_property_to_boolean( self, - property_value: Union[bool, str, Dict[str, Any]], + property_value: Union[bool, str, dict[str, Any]], property_name: str, intrinsics_resolver: IntrinsicsResolver, mappings_resolver: IntrinsicsResolver, @@ -1358,7 +1359,7 @@ def _resolve_property_to_boolean( raise InvalidResourceException(self.logical_id, f"Invalid value for property {property_name}.") def _construct_function_url( - self, lambda_function: LambdaFunction, lambda_alias: Optional[LambdaAlias], function_url_config: Dict[str, Any] + self, lambda_function: LambdaFunction, lambda_alias: LambdaAlias | None, function_url_config: dict[str, Any] ) -> LambdaUrl: """ This method is used to construct a lambda url resource @@ -1392,7 +1393,7 @@ def _construct_function_url( return lambda_url def _validate_function_url_params( - self, lambda_function: LambdaFunction, function_url_config: Dict[str, Any] + self, lambda_function: LambdaFunction, function_url_config: dict[str, Any] ) -> None: """ Validate parameters provided to configure Lambda Urls @@ -1400,7 +1401,7 @@ def _validate_function_url_params( self._validate_url_auth_type(lambda_function, function_url_config) self._validate_cors_config_parameter(lambda_function, function_url_config) - def _validate_url_auth_type(self, lambda_function: LambdaFunction, function_url_config: Dict[str, Any]) -> None: + def _validate_url_auth_type(self, lambda_function: LambdaFunction, function_url_config: dict[str, Any]) -> None: if is_intrinsic(function_url_config): return @@ -1415,7 +1416,7 @@ def _validate_url_auth_type(self, lambda_function: LambdaFunction, function_url_ ) def _validate_cors_config_parameter( - self, lambda_function: LambdaFunction, function_url_config: Dict[str, Any] + self, lambda_function: LambdaFunction, function_url_config: dict[str, Any] ) -> None: if is_intrinsic(function_url_config): return @@ -1450,8 +1451,8 @@ def _validate_cors_config_parameter( ) def _construct_url_permission( - self, lambda_function: LambdaFunction, lambda_alias: Optional[LambdaAlias], function_url_config: Dict[str, Any] - ) -> Optional[LambdaPermission]: + self, lambda_function: LambdaFunction, lambda_alias: LambdaAlias | None, function_url_config: dict[str, Any] + ) -> LambdaPermission | None: """ Construct the lambda permission associated with the function url resource in a case for public access when AuthType is NONE @@ -1465,7 +1466,7 @@ def _construct_url_permission( Lambda Alias resource - function_url_config: Dict + function_url_config: dict Function url config used to create FURL Returns @@ -1490,8 +1491,8 @@ def _construct_url_permission( return lambda_permission def _construct_invoke_permission( - self, lambda_function: LambdaFunction, lambda_alias: Optional[LambdaAlias], function_url_config: Dict[str, Any] - ) -> Optional[LambdaPermission]: + self, lambda_function: LambdaFunction, lambda_alias: LambdaAlias | None, function_url_config: dict[str, Any] + ) -> LambdaPermission | None: """ Construct the lambda permission associated with the function invoke resource in a case for public access when AuthType is NONE @@ -1504,7 +1505,7 @@ def _construct_invoke_permission( lambda_alias : LambdaAlias Lambda Alias resource - function_url_config: Dict + function_url_config: dict Function url config used to create FURL Returns @@ -1548,14 +1549,14 @@ class SamCapacityProvider(SamResourceMacro): "KmsKeyArn": Property(False, one_of(IS_STR, IS_DICT)), } - CapacityProviderName: Optional[Intrinsicable[str]] - VpcConfig: Dict[str, Any] - OperatorRole: Optional[PassThrough] - Tags: Optional[Dict[str, Any]] - PropagateTags: Optional[bool] - InstanceRequirements: Optional[Dict[str, Any]] - ScalingConfig: Optional[Dict[str, Any]] - KmsKeyArn: Optional[Intrinsicable[str]] + CapacityProviderName: Intrinsicable[str] | None + VpcConfig: dict[str, Any] + OperatorRole: PassThrough | None + Tags: dict[str, Any] | None + PropagateTags: bool | None + InstanceRequirements: dict[str, Any] | None + ScalingConfig: dict[str, Any] | None + KmsKeyArn: Intrinsicable[str] | None # Validation rules __validation_rules__ = [ @@ -1565,7 +1566,7 @@ class SamCapacityProvider(SamResourceMacro): ), ] - def to_cloudformation(self, **kwargs: Any) -> List[Resource]: + def to_cloudformation(self, **kwargs: Any) -> list[Resource]: """ Transform the SAM CapacityProvider resource to CloudFormation """ @@ -1650,37 +1651,37 @@ class SamApi(SamResourceMacro): "SecurityPolicy": PropertyType(False, IS_STR), } - Name: Optional[Intrinsicable[str]] - StageName: Optional[Intrinsicable[str]] - Tags: Optional[Dict[str, Any]] - PropagateTags: Optional[bool] - DefinitionBody: Optional[Dict[str, Any]] - DefinitionUri: Optional[Intrinsicable[str]] - MergeDefinitions: Optional[bool] - CacheClusterEnabled: Optional[Intrinsicable[bool]] - CacheClusterSize: Optional[Intrinsicable[str]] - Variables: Optional[Dict[str, Any]] - EndpointConfiguration: Optional[Dict[str, Any]] - MethodSettings: Optional[List[Any]] - BinaryMediaTypes: Optional[List[Any]] - MinimumCompressionSize: Optional[Intrinsicable[int]] - Cors: Optional[Intrinsicable[str]] - Auth: Optional[Dict[str, Any]] - GatewayResponses: Optional[Dict[str, Any]] - AccessLogSetting: Optional[Dict[str, Any]] - CanarySetting: Optional[Dict[str, Any]] - TracingEnabled: Optional[Intrinsicable[bool]] - OpenApiVersion: Optional[Intrinsicable[str]] - Models: Optional[Dict[str, Any]] - Domain: Optional[Dict[str, Any]] - FailOnWarnings: Optional[Intrinsicable[bool]] - Description: Optional[Intrinsicable[str]] - Mode: Optional[Intrinsicable[str]] - DisableExecuteApiEndpoint: Optional[Intrinsicable[bool]] - ApiKeySourceType: Optional[Intrinsicable[str]] - AlwaysDeploy: Optional[bool] - Policy: Optional[Union[Dict[str, Any], Intrinsicable[str]]] - SecurityPolicy: Optional[Intrinsicable[str]] + Name: Intrinsicable[str] | None + StageName: Intrinsicable[str] | None + Tags: dict[str, Any] | None + PropagateTags: bool | None + DefinitionBody: dict[str, Any] | None + DefinitionUri: Intrinsicable[str] | None + MergeDefinitions: bool | None + CacheClusterEnabled: Intrinsicable[bool] | None + CacheClusterSize: Intrinsicable[str] | None + Variables: dict[str, Any] | None + EndpointConfiguration: dict[str, Any] | None + MethodSettings: list[Any] | None + BinaryMediaTypes: list[Any] | None + MinimumCompressionSize: Intrinsicable[int] | None + Cors: Intrinsicable[str] | None + Auth: dict[str, Any] | None + GatewayResponses: dict[str, Any] | None + AccessLogSetting: dict[str, Any] | None + CanarySetting: dict[str, Any] | None + TracingEnabled: Intrinsicable[bool] | None + OpenApiVersion: Intrinsicable[str] | None + Models: dict[str, Any] | None + Domain: dict[str, Any] | None + FailOnWarnings: Intrinsicable[bool] | None + Description: Intrinsicable[str] | None + Mode: Intrinsicable[str] | None + DisableExecuteApiEndpoint: Intrinsicable[bool] | None + ApiKeySourceType: Intrinsicable[str] | None + AlwaysDeploy: bool | None + Policy: Union[dict[str, Any], Intrinsicable[str]] | None + SecurityPolicy: Intrinsicable[str] | None referable_properties = { "Stage": ApiGatewayStage.resource_type, @@ -1693,7 +1694,7 @@ class SamApi(SamResourceMacro): } @cw_timer - def to_cloudformation(self, **kwargs) -> List[Resource]: # type: ignore[no-untyped-def] + def to_cloudformation(self, **kwargs) -> list[Resource]: # type: ignore[no-untyped-def] """Returns the API Gateway RestApi, Deployment, and Stage to which this SAM Api corresponds. :param dict kwargs: already-converted resources that may need to be modified when converting this \ @@ -1788,22 +1789,22 @@ class SamHttpApi(SamResourceMacro): "DisableExecuteApiEndpoint": PropertyType(False, IS_BOOL), } - Name: Optional[Any] - StageName: Optional[Intrinsicable[str]] - Tags: Optional[Dict[str, Any]] - PropagateTags: Optional[bool] - DefinitionBody: Optional[Dict[str, Any]] - DefinitionUri: Optional[Intrinsicable[str]] - StageVariables: Optional[Dict[str, Intrinsicable[str]]] - CorsConfiguration: Optional[Union[bool, Dict[str, Any]]] - AccessLogSettings: Optional[Dict[str, Any]] - DefaultRouteSettings: Optional[Dict[str, Any]] - Auth: Optional[Dict[str, Any]] - RouteSettings: Optional[Dict[str, Any]] - Domain: Optional[Dict[str, Any]] - FailOnWarnings: Optional[Intrinsicable[bool]] - Description: Optional[Intrinsicable[str]] - DisableExecuteApiEndpoint: Optional[Intrinsicable[bool]] + Name: Any | None + StageName: Intrinsicable[str] | None + Tags: dict[str, Any] | None + PropagateTags: bool | None + DefinitionBody: dict[str, Any] | None + DefinitionUri: Intrinsicable[str] | None + StageVariables: dict[str, Intrinsicable[str]] | None + CorsConfiguration: Union[bool, dict[str, Any]] | None + AccessLogSettings: dict[str, Any] | None + DefaultRouteSettings: dict[str, Any] | None + Auth: dict[str, Any] | None + RouteSettings: dict[str, Any] | None + Domain: dict[str, Any] | None + FailOnWarnings: Intrinsicable[bool] | None + Description: Intrinsicable[str] | None + DisableExecuteApiEndpoint: Intrinsicable[bool] | None referable_properties = { "Stage": ApiGatewayV2Stage.resource_type, @@ -1819,7 +1820,7 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] :returns: a list of vanilla CloudFormation Resources, to which this Function expands :rtype: list """ - resources: List[Resource] = [] + resources: list[Resource] = [] intrinsics_resolver = kwargs["intrinsics_resolver"] self.CorsConfiguration = intrinsics_resolver.resolve_parameter_refs(self.CorsConfiguration) self.Domain = intrinsics_resolver.resolve_parameter_refs(self.Domain) @@ -1887,12 +1888,12 @@ class SamSimpleTable(SamResourceMacro): "SSESpecification": PropertyType(False, IS_DICT), } - PointInTimeRecoverySpecification: Optional[PassThrough] - PrimaryKey: Optional[Dict[str, str]] - ProvisionedThroughput: Optional[Dict[str, Any]] - TableName: Optional[Intrinsicable[str]] - Tags: Optional[Dict[str, Any]] - SSESpecification: Optional[Dict[str, Any]] + PointInTimeRecoverySpecification: PassThrough | None + PrimaryKey: dict[str, str] | None + ProvisionedThroughput: dict[str, Any] | None + TableName: Intrinsicable[str] | None + Tags: dict[str, Any] | None + SSESpecification: dict[str, Any] | None attribute_type_conversions = {"String": "S", "Number": "N", "Binary": "B"} @@ -1966,12 +1967,12 @@ class SamApplication(SamResourceMacro): "TimeoutInMinutes": PropertyType(False, IS_INT), } - Location: Union[str, Dict[str, Any]] - TemplateUrl: Optional[Intrinsicable[str]] - Parameters: Optional[Dict[str, Any]] - NotificationARNs: Optional[List[Any]] - Tags: Optional[Dict[str, Any]] - TimeoutInMinutes: Optional[Intrinsicable[int]] + Location: Union[str, dict[str, Any]] + TemplateUrl: Intrinsicable[str] | None + Parameters: dict[str, Any] | None + NotificationARNs: list[Any] | None + Tags: dict[str, Any] | None + TimeoutInMinutes: Intrinsicable[int] | None @cw_timer def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] @@ -1993,7 +1994,7 @@ def _construct_nested_stack(self) -> NestedStack: return nested_stack - def _get_application_tags(self) -> Dict[str, str]: + def _get_application_tags(self) -> dict[str, str]: """Adds tags to the stack if this resource is using the serverless app repo""" application_tags = {} if isinstance(self.Location, dict): @@ -2019,14 +2020,14 @@ class SamLayerVersion(SamResourceMacro): "RetentionPolicy": PropertyType(False, IS_STR), } - LayerName: Optional[Intrinsicable[str]] - Description: Optional[Intrinsicable[str]] - PublishLambdaVersion: Optional[bool] - ContentUri: Dict[str, Any] - CompatibleArchitectures: Optional[List[Any]] - CompatibleRuntimes: Optional[List[Any]] - LicenseInfo: Optional[Intrinsicable[str]] - RetentionPolicy: Optional[Intrinsicable[str]] + LayerName: Intrinsicable[str] | None + Description: Intrinsicable[str] | None + PublishLambdaVersion: bool | None + ContentUri: dict[str, Any] + CompatibleArchitectures: list[Any] | None + CompatibleRuntimes: list[Any] | None + LicenseInfo: Intrinsicable[str] | None + RetentionPolicy: Intrinsicable[str] | None retention_policy_options = [DeletionPolicy.RETAIN, DeletionPolicy.DELETE] @@ -2115,7 +2116,7 @@ def _construct_lambda_layer(self, intrinsics_resolver: IntrinsicsResolver) -> La return lambda_layer - def _get_retention_policy_value(self) -> Optional[str]: + def _get_retention_policy_value(self) -> str | None: """ Sets the deletion policy on this resource. The default is 'Retain'. @@ -2200,23 +2201,23 @@ class SamStateMachine(SamResourceMacro): "UseAliasAsEventTarget": Property(False, IS_BOOL), } - Definition: Optional[Dict[str, Any]] - DefinitionUri: Optional[Intrinsicable[str]] - Logging: Optional[Dict[str, Any]] - Role: Optional[Intrinsicable[str]] - RolePath: Optional[PassThrough] - DefinitionSubstitutions: Optional[Dict[str, Any]] - Events: Optional[Dict[str, Any]] - Name: Optional[Intrinsicable[str]] - Type: Optional[Intrinsicable[str]] - Tags: Optional[Dict[str, Any]] - PropagateTags: Optional[bool] - Policies: Optional[List[Any]] - Tracing: Optional[Dict[str, Any]] - PermissionsBoundary: Optional[Intrinsicable[str]] - AutoPublishAlias: Optional[PassThrough] - DeploymentPreference: Optional[PassThrough] - UseAliasAsEventTarget: Optional[bool] + Definition: dict[str, Any] | None + DefinitionUri: Intrinsicable[str] | None + Logging: dict[str, Any] | None + Role: Intrinsicable[str] | None + RolePath: PassThrough | None + DefinitionSubstitutions: dict[str, Any] | None + Events: dict[str, Any] | None + Name: Intrinsicable[str] | None + Type: Intrinsicable[str] | None + Tags: dict[str, Any] | None + PropagateTags: bool | None + Policies: list[Any] | None + Tracing: dict[str, Any] | None + PermissionsBoundary: Intrinsicable[str] | None + AutoPublishAlias: PassThrough | None + DeploymentPreference: PassThrough | None + UseAliasAsEventTarget: bool | None event_resolver = ResourceTypeResolver( samtranslator.model.stepfunctions.events, @@ -2264,13 +2265,13 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] return generated_resources - def resources_to_link(self, resources: Dict[str, Any]) -> Dict[str, Any]: + def resources_to_link(self, resources: dict[str, Any]) -> dict[str, Any]: try: return {"event_resources": self._event_resources_to_link(resources)} except InvalidEventException as e: raise InvalidResourceException(self.logical_id, e.message) from e - def _event_resources_to_link(self, resources: Dict[str, Any]) -> Dict[str, Any]: + def _event_resources_to_link(self, resources: dict[str, Any]) -> dict[str, Any]: event_resources = {} if self.Events: for logical_id, event_dict in self.Events.items(): @@ -2291,9 +2292,9 @@ class SamConnector(SamResourceMacro): https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html """ - Source: Dict[str, Any] - Destination: Union[Dict[str, Any], List[Dict[str, Any]]] - Permissions: List[str] + Source: dict[str, Any] + Destination: Union[dict[str, Any], list[dict[str, Any]]] + Permissions: list[str] resource_type = "AWS::Serverless::Connector" property_types = { @@ -2303,7 +2304,7 @@ class SamConnector(SamResourceMacro): } @cw_timer - def to_cloudformation(self, **kwargs: Any) -> List[Resource]: + def to_cloudformation(self, **kwargs: Any) -> list[Resource]: resource_resolver: ResourceResolver = kwargs["resource_resolver"] original_template = kwargs["original_template"] @@ -2312,7 +2313,7 @@ def to_cloudformation(self, **kwargs: Any) -> List[Resource]: multi_dest = False self.Destination = [self.Destination] - list_generated_resources: List[Resource] = [] + list_generated_resources: list[Resource] = [] for dest_index, dest in enumerate(self.Destination): try: @@ -2343,7 +2344,7 @@ def generate_resources( dest_index: int, multi_dest: bool, resource_resolver: ResourceResolver, - ) -> List[Resource]: + ) -> list[Resource]: profile = get_profile(source.resource_type, destination.resource_type) if not profile: raise InvalidResourceException( @@ -2400,7 +2401,7 @@ def generate_resources( verify_profile_variables_replaced(profile_properties) - generated_resources: List[Resource] = [] + generated_resources: list[Resource] = [] if profile_type == "AWS_IAM_ROLE_MANAGED_POLICY": generated_resources.append( self._construct_iam_policy( @@ -2425,7 +2426,7 @@ def generate_resources( raise TypeError(f"Profile type {profile_type} is not supported") return generated_resources - def _get_policy_statements(self, profile: ConnectorProfile) -> Dict[str, Any]: + def _get_policy_statements(self, profile: ConnectorProfile) -> dict[str, Any]: policy_statements = [] for name, statements in profile["AccessCategories"].items(): if name in self.Permissions: @@ -2436,7 +2437,7 @@ def _get_policy_statements(self, profile: ConnectorProfile) -> Dict[str, Any]: "Statement": policy_statements, } - def _construct_iam_policy( # noqa: PLR0913 + def _construct_iam_policy( self, source: ConnectorResourceReference, destination: ConnectorResourceReference, @@ -2484,7 +2485,7 @@ def _construct_lambda_permission_policy( profile: ConnectorProfile, dest_index: int, multi_dest: bool, - ) -> List[LambdaPermission]: + ) -> list[LambdaPermission]: source_policy = profile["SourcePolicy"] lambda_function = source if source_policy else destination @@ -2581,8 +2582,8 @@ def _construct_sqs_queue_policy( def _add_connector_metadata( self, - generated_resources: List[Resource], - original_template: Dict[str, Any], + generated_resources: list[Resource], + original_template: dict[str, Any], source: ConnectorResourceReference, destination: ConnectorResourceReference, ) -> None: @@ -2647,43 +2648,43 @@ class SamGraphQLApi(SamResourceMacro): "ResolverCountLimit": PassThroughProperty(False), } - Auth: List[Dict[str, Any]] - Tags: Optional[Dict[str, Any]] - XrayEnabled: Optional[PassThrough] - Name: Optional[PassThrough] - SchemaInline: Optional[str] - SchemaUri: Optional[str] - Logging: Optional[Union[Dict[str, Any], bool]] - DataSources: Optional[Dict[str, Dict[str, Dict[str, Any]]]] - Functions: Optional[Dict[str, Dict[str, Any]]] - Resolvers: Optional[Dict[str, Dict[str, Dict[str, Any]]]] - ApiKeys: Optional[Dict[str, Dict[str, Any]]] - DomainName: Optional[Dict[str, Any]] - Cache: Optional[Dict[str, Any]] - Visibility: Optional[PassThrough] - OwnerContact: Optional[PassThrough] - IntrospectionConfig: Optional[PassThrough] - QueryDepthLimit: Optional[PassThrough] - ResolverCountLimit: Optional[PassThrough] + Auth: list[dict[str, Any]] + Tags: dict[str, Any] | None + XrayEnabled: PassThrough | None + Name: PassThrough | None + SchemaInline: str | None + SchemaUri: str | None + Logging: Union[dict[str, Any], bool] | None + DataSources: dict[str, dict[str, dict[str, Any]]] | None + Functions: dict[str, dict[str, Any]] | None + Resolvers: dict[str, dict[str, dict[str, Any]]] | None + ApiKeys: dict[str, dict[str, Any]] | None + DomainName: dict[str, Any] | None + Cache: dict[str, Any] | None + Visibility: PassThrough | None + OwnerContact: PassThrough | None + IntrospectionConfig: PassThrough | None + QueryDepthLimit: PassThrough | None + ResolverCountLimit: PassThrough | None # stop validation so we can use class variables for tracking state validate_setattr = False def __init__( self, - logical_id: Optional[Any], - relative_id: Optional[str] = None, - depends_on: Optional[List[str]] = None, - attributes: Optional[Dict[str, Any]] = None, + logical_id: Any | None, + relative_id: str | None = None, + depends_on: list[str] | None = None, + attributes: dict[str, Any] | None = None, ): super().__init__(logical_id, relative_id=relative_id, depends_on=depends_on, attributes=attributes) - self._none_datasource: Optional[DataSource] = None - self._datasource_name_map: Dict[str, Intrinsicable[str]] = {} - self._function_id_map: Dict[str, Intrinsicable[str]] = {} + self._none_datasource: DataSource | None = None + self._datasource_name_map: dict[str, Intrinsicable[str]] = {} + self._function_id_map: dict[str, Intrinsicable[str]] = {} @cw_timer - def to_cloudformation(self, **kwargs: Any) -> List[Resource]: + def to_cloudformation(self, **kwargs: Any) -> list[Resource]: check_python_314_compatibility(aws_serverless_graphqlapi, "GraphQLApi") model = self.validate_properties_and_return_model(aws_serverless_graphqlapi.Properties) @@ -2691,7 +2692,7 @@ def to_cloudformation(self, **kwargs: Any) -> List[Resource]: api_id = appsync_api.get_runtime_attr("api_id") appsync_schema = self._construct_appsync_schema(model, api_id) - resources: List[Resource] = [appsync_api, appsync_schema] + resources: list[Resource] = [appsync_api, appsync_schema] for connector in auth_connectors: resources.extend(connector.to_cloudformation(**kwargs)) @@ -2732,7 +2733,7 @@ def to_cloudformation(self, **kwargs: Any) -> List[Resource]: def _construct_appsync_api_resources( self, model: aws_serverless_graphqlapi.Properties - ) -> Tuple[GraphQLApi, Optional[IAMRole], List[SamConnector]]: + ) -> tuple[GraphQLApi, IAMRole | None, list[SamConnector]]: api = GraphQLApi(logical_id=self.logical_id, depends_on=self.depends_on, attributes=self.resource_attributes) api.Name = passthrough_value(model.Name) or self.logical_id @@ -2774,14 +2775,14 @@ def _construct_appsync_api_resources( def _parse_and_set_auth_properties( self, api: GraphQLApi, auth: aws_serverless_graphqlapi.Auth - ) -> List[Intrinsicable[str]]: + ) -> list[Intrinsicable[str]]: """ Parse the Auth properties in a Serverless::GraphQLApi resource. - Returns: List of Lambda Function arns of Lambda authorizers. If no Lambda authorizer is used, the list is empty. + Returns: list of Lambda Function arns of Lambda authorizers. If no Lambda authorizer is used, the list is empty. """ # Keep all lambda authorizers together to create connectors later - lambda_auth_arns: List[Intrinsicable[str]] = [] + lambda_auth_arns: list[Intrinsicable[str]] = [] # Default authoriser default_auth = aws_serverless_graphqlapi.Authorizer.parse_obj( @@ -2800,7 +2801,7 @@ def _parse_and_set_auth_properties( lambda_auth_arns.append(cast(LambdaAuthorizerConfigType, auth_dict)["AuthorizerUri"]) # Additional authentication - additional_auths: List[AdditionalAuthenticationProviderType] = [] + additional_auths: list[AdditionalAuthenticationProviderType] = [] if auth.Additional: for index, additional in enumerate(auth.Additional): name, auth_dict = self._validate_and_extract_authorizer_config(additional, index) @@ -2820,10 +2821,10 @@ def _parse_and_set_auth_properties( def _validate_and_extract_authorizer_config( self, auth: aws_serverless_graphqlapi.Authorizer, - index: Optional[int] = None, - ) -> Tuple[ - Optional[Literal["LambdaAuthorizerConfig", "OpenIDConnectConfig", "UserPoolConfig"]], - Optional[Union[LambdaAuthorizerConfigType, OpenIDConnectConfigType, UserPoolConfigType]], + index: int | None = None, + ) -> tuple[ + Literal["LambdaAuthorizerConfig", "OpenIDConnectConfig", "UserPoolConfig"] | None, + Union[LambdaAuthorizerConfigType, OpenIDConnectConfigType, UserPoolConfigType] | None, ]: """ Validates the authentication type and returns the name of the config property and the respective dictionary. @@ -2901,7 +2902,7 @@ def _construct_lambda_auth_connector( SamConnector(logical_id=logical_id).from_dict(logical_id=logical_id, resource_dict=connector_dict), ) - def _create_logging_default(self) -> Tuple[LogConfigType, IAMRole]: + def _create_logging_default(self) -> tuple[LogConfigType, IAMRole]: """ Create a default logging configuration. @@ -2916,7 +2917,7 @@ def _create_logging_default(self) -> Tuple[LogConfigType, IAMRole]: def _parse_logging_properties( self, model: aws_serverless_graphqlapi.Properties - ) -> Tuple[LogConfigType, Optional[IAMRole]]: + ) -> tuple[LogConfigType, IAMRole | None]: """Parse logging properties from SAM template, and use defaults if required keys dont exist.""" if not isinstance(model.Logging, aws_serverless_graphqlapi.Logging): return self._create_logging_default() @@ -2973,9 +2974,9 @@ def _construct_appsync_schema( return schema def _construct_appsync_api_keys( - self, api_keys: Dict[str, aws_serverless_graphqlapi.ApiKey], api_id: Intrinsicable[str] - ) -> List[Resource]: - resources: List[Resource] = [] + self, api_keys: dict[str, aws_serverless_graphqlapi.ApiKey], api_id: Intrinsicable[str] + ) -> list[Resource]: + resources: list[Resource] = [] # TODO: Add datetime parsing for ExpiresOn; currently expects Unix timestamp for relative_id, api_key in api_keys.items(): @@ -2994,7 +2995,7 @@ def _construct_appsync_api_keys( def _construct_domain_name_resources( self, domain_name: aws_serverless_graphqlapi.DomainName, api_id: Intrinsicable[str] - ) -> List[Resource]: + ) -> list[Resource]: cfn_domain_name = DomainName( logical_id=f"{self.logical_id}DomainName", depends_on=self.depends_on, attributes=self.resource_attributes ) @@ -3032,8 +3033,8 @@ def _construct_datasource_resources( self, datasources: aws_serverless_graphqlapi.DataSources, api_id: Intrinsicable[str], - kwargs: Dict[str, Any], - ) -> List[Resource]: + kwargs: dict[str, Any], + ) -> list[Resource]: ddb_datasources = self._construct_ddb_datasources(datasources.DynamoDb, api_id, kwargs) lambda_datasources = self._construct_lambda_datasources(datasources.Lambda, api_id, kwargs) @@ -3041,14 +3042,14 @@ def _construct_datasource_resources( def _construct_ddb_datasources( self, - ddb_datasources: Optional[Dict[str, aws_serverless_graphqlapi.DynamoDBDataSource]], + ddb_datasources: dict[str, aws_serverless_graphqlapi.DynamoDBDataSource] | None, api_id: Intrinsicable[str], - kwargs: Dict[str, Any], - ) -> List[Resource]: + kwargs: dict[str, Any], + ) -> list[Resource]: if not ddb_datasources: return [] - resources: List[Resource] = [] + resources: list[Resource] = [] for relative_id, ddb_datasource in ddb_datasources.items(): datasource_logical_id = self._create_appsync_data_source_logical_id( @@ -3080,8 +3081,8 @@ def _parse_ddb_datasource_role( ddb_datasource: aws_serverless_graphqlapi.DynamoDBDataSource, datasource_arn: Intrinsicable[str], datasource_logical_id: str, - kwargs: Dict[str, Any], - ) -> Tuple[str, List[Resource]]: + kwargs: dict[str, Any], + ) -> tuple[str, list[Resource]]: # If the user defined a role, then there's no need to generate role/policy for them, so we return fast. if ddb_datasource.ServiceRoleArn: return cast(PassThrough, ddb_datasource.ServiceRoleArn), [] @@ -3135,8 +3136,8 @@ def _construct_ddb_datasource_connector_resources( destination_arn: Intrinsicable[str], permissions: PermissionsType, role_name: Intrinsicable[str], - kwargs: Dict[str, Any], - ) -> List[Resource]: + kwargs: dict[str, Any], + ) -> list[Resource]: logical_id = f"{datasource_id}ToTableConnector" connector_dict = { "Type": "AWS::Serverless::Connector", @@ -3159,14 +3160,14 @@ def _construct_ddb_datasource_connector_resources( def _construct_lambda_datasources( self, - lambda_datasources: Optional[Dict[str, aws_serverless_graphqlapi.LambdaDataSource]], + lambda_datasources: dict[str, aws_serverless_graphqlapi.LambdaDataSource] | None, api_id: Intrinsicable[str], - kwargs: Dict[str, Any], - ) -> List[Resource]: + kwargs: dict[str, Any], + ) -> list[Resource]: if not lambda_datasources: return [] - resources: List[Resource] = [] + resources: list[Resource] = [] for relative_id, lambda_datasource in lambda_datasources.items(): datasource_logical_id = self._create_appsync_data_source_logical_id(self.logical_id, "Lambda", relative_id) @@ -3200,8 +3201,8 @@ def _parse_lambda_datasource_role( datasource_arn: Intrinsicable[str], function_arn: PassThrough, datasource_logical_id: str, - kwargs: Dict[str, Any], - ) -> Tuple[str, List[Resource]]: + kwargs: dict[str, Any], + ) -> tuple[str, list[Resource]]: if lambda_datasource.ServiceRoleArn: return passthrough_value(lambda_datasource.ServiceRoleArn), [] @@ -3228,8 +3229,8 @@ def _construct_lambda_datasource_connector_resources( source_arn: Intrinsicable[str], destination_arn: Intrinsicable[str], role_name: Intrinsicable[str], - kwargs: Dict[str, Any], - ) -> List[Resource]: + kwargs: dict[str, Any], + ) -> list[Resource]: logical_id = f"{datasource_id}ToLambdaConnector" connector_dict = { "Type": "AWS::Serverless::Connector", @@ -3253,10 +3254,10 @@ def _construct_lambda_datasource_connector_resources( def _construct_appsync_function_configurations( self, - functions: Dict[str, aws_serverless_graphqlapi.Function], + functions: dict[str, aws_serverless_graphqlapi.Function], api_id: Intrinsicable[str], - ) -> List[FunctionConfiguration]: - func_configs: List[FunctionConfiguration] = [] + ) -> list[FunctionConfiguration]: + func_configs: list[FunctionConfiguration] = [] for relative_id, function in functions.items(): # "Id" refers to the "FunctionId" attribute for a "AppSync::FunctionConfiguration" resource. @@ -3294,7 +3295,7 @@ def _construct_appsync_function_configurations( return func_configs @staticmethod - def _is_none_datasource_input(datasource: Optional[str]) -> bool: + def _is_none_datasource_input(datasource: str | None) -> bool: return datasource is not None and datasource.lower() == "none" def _construct_none_datasource( @@ -3359,13 +3360,13 @@ def _parse_datasource_name( # if DataSource is intrinsic function like !GetAttr AppSyncDataSource.Name # but it can also be ImportValue or Sub or maybe something else - return function.DataSource # it's an intrinsic function Dict here + return function.DataSource # it's an intrinsic function dict here @staticmethod def _parse_function_code_properties( function: aws_serverless_graphqlapi.Function, relative_id: str, - ) -> Tuple[Optional[PassThrough], Optional[PassThrough]]: + ) -> tuple[PassThrough | None, PassThrough | None]: """ Parses the code properties from Serverless::GraphQLApi function. @@ -3405,11 +3406,11 @@ def _parse_runtime( def _construct_appsync_resolver_resources( self, - resolvers: Dict[str, Dict[str, aws_serverless_graphqlapi.Resolver]], + resolvers: dict[str, dict[str, aws_serverless_graphqlapi.Resolver]], api_id: Intrinsicable[str], schema_logical_id: str, - ) -> List[Resource]: - resources: List[Resource] = [] + ) -> list[Resource]: + resources: list[Resource] = [] for type_name, relative_id_to_resolver in resolvers.items(): for relative_id, resolver in relative_id_to_resolver.items(): @@ -3461,7 +3462,7 @@ def _construct_appsync_resolver_resources( def _parse_appsync_resolver_functions( self, appsync_resolver: aws_serverless_graphqlapi.Resolver, relative_id: str - ) -> List[Intrinsicable[str]]: + ) -> list[Intrinsicable[str]]: """ Parse functions property in GraphQLApi Resolver. diff --git a/samtranslator/model/scheduler.py b/samtranslator/model/scheduler.py index a92dc73fe4..53d31e0359 100644 --- a/samtranslator/model/scheduler.py +++ b/samtranslator/model/scheduler.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Any from samtranslator.model import GeneratedProperty, Resource from samtranslator.model.intrinsics import fnGetAtt @@ -23,14 +23,14 @@ class SchedulerSchedule(Resource): ScheduleExpression: PassThrough FlexibleTimeWindow: PassThrough - Name: Optional[PassThrough] - State: Optional[PassThrough] - Description: Optional[PassThrough] - StartDate: Optional[PassThrough] - EndDate: Optional[PassThrough] - ScheduleExpressionTimezone: Optional[PassThrough] - GroupName: Optional[PassThrough] - KmsKeyArn: Optional[PassThrough] - Target: Dict[str, Any] + Name: PassThrough | None + State: PassThrough | None + Description: PassThrough | None + StartDate: PassThrough | None + EndDate: PassThrough | None + ScheduleExpressionTimezone: PassThrough | None + GroupName: PassThrough | None + KmsKeyArn: PassThrough | None + Target: dict[str, Any] runtime_attrs = {"arn": lambda self: fnGetAtt(self.logical_id, "Arn")} diff --git a/samtranslator/model/sqs.py b/samtranslator/model/sqs.py index d829b43625..50370855f4 100644 --- a/samtranslator/model/sqs.py +++ b/samtranslator/model/sqs.py @@ -1,5 +1,3 @@ -from typing import Dict - from samtranslator.model import GeneratedProperty, PropertyType, Resource from samtranslator.model.intrinsics import fnGetAtt, ref from samtranslator.model.types import PassThrough @@ -7,7 +5,7 @@ class SQSQueue(Resource): resource_type = "AWS::SQS::Queue" - property_types: Dict[str, PropertyType] = { + property_types: dict[str, PropertyType] = { "FifoQueue": GeneratedProperty(), "Tags": GeneratedProperty(), } diff --git a/samtranslator/model/stepfunctions/__init__.py b/samtranslator/model/stepfunctions/__init__.py index 2ce3fe4ea6..ff78e1a4c9 100644 --- a/samtranslator/model/stepfunctions/__init__.py +++ b/samtranslator/model/stepfunctions/__init__.py @@ -1,8 +1,8 @@ __all__ = [ + "StateMachineGenerator", "StepFunctionsStateMachine", - "StepFunctionsStateMachineVersion", "StepFunctionsStateMachineAlias", - "StateMachineGenerator", + "StepFunctionsStateMachineVersion", "events", ] diff --git a/samtranslator/model/stepfunctions/events.py b/samtranslator/model/stepfunctions/events.py index d2f8500b40..b85ab9f87b 100644 --- a/samtranslator/model/stepfunctions/events.py +++ b/samtranslator/model/stepfunctions/events.py @@ -1,6 +1,6 @@ import json from abc import ABCMeta -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Union, cast from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model import Property, PropertyType, Resource, ResourceMacro @@ -32,7 +32,7 @@ class EventSource(ResourceMacro, metaclass=ABCMeta): principal: str = None # type: ignore relative_id: str # overriding the Optional[str]: for event, relative id is not None - Target: Optional[Dict[str, str]] + Target: dict[str, str] | None def _generate_logical_id(self, prefix, suffix, resource_type): # type: ignore[no-untyped-def] """Helper utility to generate a logicial ID for a new resource @@ -54,8 +54,8 @@ def _generate_logical_id(self, prefix, suffix, resource_type): # type: ignore[n def _construct_role( self, resource: StepFunctionsStateMachine, - permissions_boundary: Optional[str], - prefix: Optional[str], + permissions_boundary: str | None, + prefix: str | None, suffix: str = "", ) -> IAMRole: """Constructs the IAM Role resource allowing the event service to invoke @@ -104,15 +104,15 @@ class Schedule(EventSource): } Schedule: PassThrough - Input: Optional[PassThrough] - Enabled: Optional[bool] - State: Optional[PassThrough] - Name: Optional[PassThrough] - Description: Optional[PassThrough] - DeadLetterConfig: Optional[Dict[str, Any]] - RetryPolicy: Optional[PassThrough] - Target: Optional[PassThrough] - RoleArn: Optional[PassThrough] + Input: PassThrough | None + Enabled: bool | None + State: PassThrough | None + Name: PassThrough | None + Description: PassThrough | None + DeadLetterConfig: dict[str, Any] | None + RetryPolicy: PassThrough | None + Target: PassThrough | None + RoleArn: PassThrough | None @cw_timer(prefix=SFN_EVETSOURCE_METRIC_PREFIX) def to_cloudformation(self, resource, **kwargs): # type: ignore[no-untyped-def] @@ -122,7 +122,7 @@ def to_cloudformation(self, resource, **kwargs): # type: ignore[no-untyped-def] :returns: a list of vanilla CloudFormation Resources, to which this Schedule event expands :rtype: list """ - resources: List[Any] = [] + resources: list[Any] = [] permissions_boundary = kwargs.get("permissions_boundary") @@ -144,7 +144,7 @@ def to_cloudformation(self, resource, **kwargs): # type: ignore[no-untyped-def] events_rule.Name = self.Name events_rule.Description = self.Description - role: Union[IAMRole, str, Dict[str, Any]] + role: Union[IAMRole, str, dict[str, Any]] if self.RoleArn is None: role = self._construct_role(resource, permissions_boundary, prefix=None) resources.append(role) @@ -166,9 +166,9 @@ def to_cloudformation(self, resource, **kwargs): # type: ignore[no-untyped-def] def _construct_target( self, resource: StepFunctionsStateMachine, - role: Union[IAMRole, str, Dict[str, Any]], - dead_letter_queue_arn: Optional[str], - ) -> Dict[str, Any]: + role: Union[IAMRole, str, dict[str, Any]], + dead_letter_queue_arn: str | None, + ) -> dict[str, Any]: """_summary_ Parameters @@ -227,16 +227,16 @@ class CloudWatchEvent(EventSource): "InputTransformer": PropertyType(False, IS_DICT), } - EventBusName: Optional[PassThrough] - RuleName: Optional[PassThrough] - Pattern: Optional[PassThrough] - Input: Optional[PassThrough] - InputPath: Optional[PassThrough] - DeadLetterConfig: Optional[Dict[str, Any]] - RetryPolicy: Optional[PassThrough] - State: Optional[PassThrough] - Target: Optional[PassThrough] - InputTransformer: Optional[PassThrough] + EventBusName: PassThrough | None + RuleName: PassThrough | None + Pattern: PassThrough | None + Input: PassThrough | None + InputPath: PassThrough | None + DeadLetterConfig: dict[str, Any] | None + RetryPolicy: PassThrough | None + State: PassThrough | None + Target: PassThrough | None + InputTransformer: PassThrough | None @cw_timer(prefix=SFN_EVETSOURCE_METRIC_PREFIX) def to_cloudformation(self, resource, **kwargs): # type: ignore[no-untyped-def] @@ -247,7 +247,7 @@ def to_cloudformation(self, resource, **kwargs): # type: ignore[no-untyped-def] :returns: a list of vanilla CloudFormation Resources, to which this CloudWatch Events/EventBridge event expands :rtype: list """ - resources: List[Any] = [] + resources: list[Any] = [] permissions_boundary = kwargs.get("permissions_boundary") @@ -340,11 +340,11 @@ class Api(EventSource): Path: str Method: str RestApiId: str - Stage: Optional[str] - Auth: Optional[Dict[str, Any]] - UnescapeMappingTemplate: Optional[bool] + Stage: str | None + Auth: dict[str, Any] | None + UnescapeMappingTemplate: bool | None - def resources_to_link(self, resources: Dict[str, Any]) -> Dict[str, Any]: + def resources_to_link(self, resources: dict[str, Any]) -> dict[str, Any]: """ If this API Event Source refers to an explicit API resource, resolve the reference and grab necessary data from the explicit API @@ -366,7 +366,7 @@ def to_cloudformation(self, resource, **kwargs): # type: ignore[no-untyped-def] :returns: a list of vanilla CloudFormation Resources, to which this Api event expands :rtype: list """ - resources: List[Any] = [] + resources: list[Any] = [] intrinsics_resolver = kwargs.get("intrinsics_resolver") permissions_boundary = kwargs.get("permissions_boundary") @@ -436,7 +436,7 @@ def _add_swagger_integration(self, api, api_id, resource, role, intrinsics_resol api["DefinitionBody"] = editor.swagger - def _generate_request_template(self, resource: Resource) -> Dict[str, Any]: + def _generate_request_template(self, resource: Resource) -> dict[str, Any]: """Generates the Body mapping request template for the Api. This allows for the input request to the Api to be passed as the execution input to the associated state machine resource. @@ -457,7 +457,7 @@ def _generate_request_template(self, resource: Resource) -> Dict[str, Any]: ) } - def _generate_request_template_unescaped(self, resource: Resource) -> Dict[str, Any]: + def _generate_request_template_unescaped(self, resource: Resource) -> dict[str, Any]: """Generates the Body mapping request template for the Api. This allows for the input request to the Api to be passed as the execution input to the associated state machine resource. diff --git a/samtranslator/model/stepfunctions/generators.py b/samtranslator/model/stepfunctions/generators.py index 0c443a4788..6ed1ca301c 100644 --- a/samtranslator/model/stepfunctions/generators.py +++ b/samtranslator/model/stepfunctions/generators.py @@ -1,6 +1,6 @@ import json from copy import deepcopy -from typing import Any, Dict, List, Tuple +from typing import Any from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model.exceptions import InvalidEventException, InvalidResourceException @@ -74,7 +74,7 @@ def __init__( # type: ignore[no-untyped-def] # noqa: PLR0913 :param role_path: The file path of the execution role :param state_machine_type: Type of the State Machine :param tracing: Tracing configuration for the State Machine - :param events: List of event sources for the State Machine + :param events: list of event sources for the State Machine :param event_resources: Event resources to link :param event_resolver: Resolver that maps Event types to Event classes :param tags: Tags to be associated with the State Machine resource @@ -122,7 +122,7 @@ def to_cloudformation(self): # type: ignore[no-untyped-def] :returns: a list of resources including the State Machine resource. :rtype: list """ - resources: List[Any] = [self.state_machine] + resources: list[Any] = [self.state_machine] # Defaulting to {} will add the DefinitionSubstitutions field on the transform output even when it is not relevant if self.definition_substitutions: @@ -173,7 +173,7 @@ def to_cloudformation(self): # type: ignore[no-untyped-def] return resources - def _construct_definition_uri(self) -> Dict[str, Any]: + def _construct_definition_uri(self) -> dict[str, Any]: """ Constructs the State Machine's `DefinitionS3 property`_, from the SAM State Machines's DefinitionUri property. @@ -247,11 +247,11 @@ def _construct_role(self) -> IAMRole: get_managed_policy_map=self.get_managed_policy_map, ) - def _construct_tag_list(self) -> List[Dict[str, Any]]: + def _construct_tag_list(self) -> list[dict[str, Any]]: """ Transforms the SAM defined Tags into the form CloudFormation is expecting. - :returns: List of Tag Dictionaries + :returns: list of Tag Dictionaries :rtype: list """ sam_tag = {self._SAM_KEY: self._SAM_VALUE} @@ -309,7 +309,7 @@ def _construct_alias(self, version: StepFunctionsStateMachineVersion) -> StepFun def _generate_managed_traffic_shifting_resources( self, - ) -> List[Any]: + ) -> list[Any]: """Generates and returns the version and alias resources associated with this state machine's managed traffic shifting. :returns: a list containing the state machine's version and alias resources @@ -329,7 +329,7 @@ def _generate_managed_traffic_shifting_resources( state_machine_version = self._construct_version() return [state_machine_version, self._construct_alias(state_machine_version)] - def _generate_event_resources(self) -> List[Dict[str, Any]]: + def _generate_event_resources(self) -> list[dict[str, Any]]: """Generates and returns the resources associated with this state machine's event sources. :returns: a list containing the state machine's event resources @@ -365,7 +365,7 @@ def _replace_dynamic_values_with_substitutions(self, _input): # type: ignore[no :param _input: Input dictionary in which the dynamic values need to be replaced with substitutions - :returns: List of substitution to dynamic value mappings + :returns: list of substitution to dynamic value mappings :rtype: dict """ substitution_map = {} @@ -384,7 +384,7 @@ def _get_paths_to_intrinsics(self, _input, path=None): # type: ignore[no-untype :param _input: Input dictionary to find paths to dynamic values in :param path: Optional list to keep track of the path to the input dictionary - :returns list: List of keys that defines the path to a dynamic value within the input dictionary + :returns list: list of keys that defines the path to a dynamic value within the input dictionary """ if path is None: path = [] @@ -404,7 +404,7 @@ def _get_paths_to_intrinsics(self, _input, path=None): # type: ignore[no-untype return dynamic_value_paths - def _generate_substitution(self) -> Tuple[str, str]: + def _generate_substitution(self) -> tuple[str, str]: """ Generates a name and key for a new substitution. diff --git a/samtranslator/model/stepfunctions/resources.py b/samtranslator/model/stepfunctions/resources.py index 7cb07ba902..3d8303695a 100644 --- a/samtranslator/model/stepfunctions/resources.py +++ b/samtranslator/model/stepfunctions/resources.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any from samtranslator.model import GeneratedProperty, Resource from samtranslator.model.intrinsics import fnGetAtt, ref @@ -19,16 +19,16 @@ class StepFunctionsStateMachine(Resource): "TracingConfiguration": GeneratedProperty(), } - Definition: Optional[Dict[str, Any]] - DefinitionString: Optional[str] - DefinitionS3Location: Optional[Dict[str, Any]] - LoggingConfiguration: Optional[Dict[str, Any]] + Definition: dict[str, Any] | None + DefinitionString: str | None + DefinitionS3Location: dict[str, Any] | None + LoggingConfiguration: dict[str, Any] | None RoleArn: str - StateMachineName: Optional[str] - StateMachineType: Optional[str] - Tags: Optional[List[Dict[str, Any]]] - DefinitionSubstitutions: Optional[Dict[str, Any]] - TracingConfiguration: Optional[Dict[str, Any]] + StateMachineName: str | None + StateMachineType: str | None + Tags: list[dict[str, Any]] | None + DefinitionSubstitutions: dict[str, Any] | None + TracingConfiguration: dict[str, Any] | None runtime_attrs = { "arn": lambda self: ref(self.logical_id), diff --git a/samtranslator/model/tags/resource_tagging.py b/samtranslator/model/tags/resource_tagging.py index 788bfc46d3..6a7c2ffe5e 100644 --- a/samtranslator/model/tags/resource_tagging.py +++ b/samtranslator/model/tags/resource_tagging.py @@ -1,11 +1,11 @@ # Constants for Tagging -from typing import Any, Dict, List, Optional +from typing import Any _KEY = "Key" _VALUE = "Value" -def get_tag_list(resource_tag_dict: Optional[Dict[str, Any]]) -> List[Dict[str, Any]]: +def get_tag_list(resource_tag_dict: dict[str, Any] | None) -> list[dict[str, Any]]: """ Transforms the SAM defined Tags into the form CloudFormation is expecting. @@ -23,7 +23,7 @@ def get_tag_list(resource_tag_dict: Optional[Dict[str, Any]]) -> List[Dict[str, ``` :param resource_tag_dict: Customer defined dictionary (SAM Example from above) - :return: List of Tag Dictionaries (CloudFormation Equivalent from above) + :return: list of Tag Dictionaries (CloudFormation Equivalent from above) """ tag_list = [] # type: ignore[var-annotated] if resource_tag_dict is None: diff --git a/samtranslator/model/types.py b/samtranslator/model/types.py index 522def9a59..1f015ab9e4 100644 --- a/samtranslator/model/types.py +++ b/samtranslator/model/types.py @@ -9,7 +9,8 @@ either a string or a list of strings, but do not validate whether the string(s) are valid IAM policy ARNs. """ -from typing import Any, Callable, List, Type, Union +from collections.abc import Callable +from typing import Any, Union import samtranslator.model.exceptions from samtranslator.internal.deprecation_control import deprecated @@ -23,7 +24,7 @@ Validator = Callable[..., bool] -def is_type(valid_type: Type[Any]) -> Validator: +def is_type(valid_type: type[Any]) -> Validator: """Returns a validator function that succeeds only for inputs of the provided valid_type. :param type valid_type: the type that should be considered valid for the validator @@ -48,7 +49,7 @@ def validate(value: Any, should_raise: bool = True) -> bool: IS_INT = is_type(int) -def list_of(validate_item: Union[Type[Any], Validator]) -> Validator: +def list_of(validate_item: Union[type[Any], Validator]) -> Validator: """Returns a validator function that succeeds only if the input is a list, and each item in the list passes as input to the provided validator validate_item. @@ -138,7 +139,7 @@ def validate(value: Any, should_raise: bool = False) -> bool: return validate -def IS_STR_ENUM(valid_values: List[str]) -> Validator: +def IS_STR_ENUM(valid_values: list[str]) -> Validator: """Returns a validator function that succeeds only if the input is a string matching one of the valid enum values. :param list valid_values: the valid string values for the enum diff --git a/samtranslator/model/update_policy.py b/samtranslator/model/update_policy.py index de97c3cd2b..2ae1b88670 100644 --- a/samtranslator/model/update_policy.py +++ b/samtranslator/model/update_policy.py @@ -1,5 +1,5 @@ from collections import namedtuple -from typing import Any, Dict +from typing import Any from samtranslator.model.intrinsics import ref @@ -22,7 +22,7 @@ class UpdatePolicy(CodeDeployLambdaAliasUpdate): - def to_dict(self) -> Dict[str, Dict[str, Any]]: + def to_dict(self) -> dict[str, dict[str, Any]]: """ :return: a dict that can be used as part of a cloudformation template """ diff --git a/samtranslator/open_api/base_editor.py b/samtranslator/open_api/base_editor.py index e5a9ef26ef..760a95d290 100644 --- a/samtranslator/open_api/base_editor.py +++ b/samtranslator/open_api/base_editor.py @@ -1,7 +1,8 @@ """Base class for OpenApiEditor and SwaggerEditor.""" import re -from typing import Any, Dict, Iterator, List, Optional, Union +from collections.abc import Iterator +from typing import Any, Union from samtranslator.model.apigateway import ApiGatewayAuthorizer from samtranslator.model.apigatewayv2 import ApiGatewayV2Authorizer @@ -21,11 +22,11 @@ class BaseEditor: _OPENAPI_VERSION_3_REGEX = r"\A3(\.\d)(\.\d)?$" # attributes: - _doc: Dict[str, Any] - paths: Dict[str, Any] + _doc: dict[str, Any] + paths: dict[str, Any] @staticmethod - def get_conditional_contents(item: Any) -> List[Any]: + def get_conditional_contents(item: Any) -> list[Any]: """ Returns the contents of the given item. If a conditional block has been used inside the item, returns a list of the content @@ -47,7 +48,7 @@ def get_conditional_contents(item: Any) -> List[Any]: return contents @staticmethod - def method_definition_has_integration(method_definition: Dict[str, Any]) -> bool: + def method_definition_has_integration(method_definition: dict[str, Any]) -> bool: """ Checks a method definition to make sure it has an apigw integration @@ -56,7 +57,7 @@ def method_definition_has_integration(method_definition: Dict[str, Any]) -> bool """ return bool(method_definition.get(BaseEditor._X_APIGW_INTEGRATION)) - def method_has_integration(self, raw_method_definition: Dict[str, Any], path: str, method: str) -> bool: + def method_has_integration(self, raw_method_definition: dict[str, Any], path: str, method: str) -> bool: """ Returns true if the given method contains a valid method definition. This uses the get_conditional_contents function to handle conditionals. @@ -110,7 +111,7 @@ def _normalize_method_name(method: Any) -> Any: return BaseEditor._X_ANY_METHOD return method - def has_path(self, path: str, method: Optional[str] = None) -> bool: + def has_path(self, path: str, method: str | None = None) -> bool: """ Returns True if this Swagger has the given path and optional method For paths with conditionals, only returns true if both items (true case, and false case) have the method. @@ -153,7 +154,7 @@ def has_integration(self, path: str, method: str) -> bool: # Integration present and non-empty return True - def add_path(self, path: str, method: Optional[str] = None) -> None: + def add_path(self, path: str, method: str | None = None) -> None: """ Adds the path/method combination to the Swagger, if not already present @@ -174,7 +175,7 @@ def add_path(self, path: str, method: Optional[str] = None) -> None: for path_item in self.get_conditional_contents(path_dict): path_item.setdefault(method, Py27Dict()) - def add_timeout_to_method(self, api: Dict[str, Any], path: str, method_name: str, timeout: int) -> None: + def add_timeout_to_method(self, api: dict[str, Any], path: str, method_name: str, timeout: int) -> None: """ Adds a timeout to the path/method. @@ -189,7 +190,7 @@ def add_timeout_to_method(self, api: Dict[str, Any], path: str, method_name: str @staticmethod def _get_authorization_scopes( - authorizers: Union[Dict[str, ApiGatewayAuthorizer], Dict[str, ApiGatewayV2Authorizer]], default_authorizer: str + authorizers: Union[dict[str, ApiGatewayAuthorizer], dict[str, ApiGatewayV2Authorizer]], default_authorizer: str ) -> Any: """ Returns auth scopes for an authorizer if present @@ -203,7 +204,7 @@ def _get_authorization_scopes( def iter_on_method_definitions_for_path_at_method( self, path_name: str, method_name: str, skip_methods_without_apigw_integration: bool = True - ) -> Iterator[Dict[str, Any]]: + ) -> Iterator[dict[str, Any]]: """ Yields all the method definitions for the path+method combinations if path and/or method have IF conditionals. If there are no conditionals, will just yield the single method definition at the given path and method name. @@ -251,7 +252,7 @@ def validate_path_item_is_dict(path_item: Any, path: str) -> None: ) @staticmethod - def validate_method_definition_is_dict(method_definition: Optional[Any], path: str, method: str) -> None: + def validate_method_definition_is_dict(method_definition: Any | None, path: str, method: str) -> None: BaseEditor.validate_is_dict( method_definition, f"Definition of method '{method}' for path '{path}' should be a map." ) diff --git a/samtranslator/open_api/open_api.py b/samtranslator/open_api/open_api.py index 8ced511649..5fd785c02c 100644 --- a/samtranslator/open_api/open_api.py +++ b/samtranslator/open_api/open_api.py @@ -1,7 +1,8 @@ import copy import json import re -from typing import Any, Callable, Dict, Optional, TypeVar +from collections.abc import Callable +from typing import Any, TypeVar from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model.apigatewayv2 import ApiGatewayV2Authorizer @@ -40,9 +41,9 @@ class OpenApiEditor(BaseEditor): _DEFAULT_OPENAPI_TITLE = ref("AWS::StackName") # Attributes: - _doc: Dict[str, Any] + _doc: dict[str, Any] - def __init__(self, doc: Optional[Dict[str, Any]]) -> None: + def __init__(self, doc: dict[str, Any] | None) -> None: """ Initialize the class with a swagger dictionary. This class creates a copy of the Swagger and performs all modifications on this copy. @@ -234,7 +235,7 @@ def add_payload_format_version_to_method(self, api, path, method_name, payload_f for method_definition in self.iter_on_method_definitions_for_path_at_method(path, method_name): method_definition[self._X_APIGW_INTEGRATION]["payloadFormatVersion"] = payload_format_version - def add_authorizers_security_definitions(self, authorizers: Dict[str, ApiGatewayV2Authorizer]) -> None: + def add_authorizers_security_definitions(self, authorizers: dict[str, ApiGatewayV2Authorizer]) -> None: """ Add Authorizer definitions to the securityDefinitions part of Swagger. @@ -249,7 +250,7 @@ def set_path_default_authorizer( self, path: str, default_authorizer: str, - authorizers: Dict[str, ApiGatewayV2Authorizer], + authorizers: dict[str, ApiGatewayV2Authorizer], ) -> None: """ Adds the default_authorizer to the security block for each method on this path unless an Authorizer @@ -260,7 +261,7 @@ def set_path_default_authorizer( :param string path: Path name :param string default_authorizer: Name of the authorizer to use as the default. Must be a key in the authorizers param. - :param dict authorizers: Dict of Authorizer configurations defined on the related Api. + :param dict authorizers: dict of Authorizer configurations defined on the related Api. """ for path_item in self.get_conditional_contents(self.paths.get(path)): BaseEditor.validate_path_item_is_dict(path_item, path) @@ -361,7 +362,7 @@ def _set_method_authorizer(self, path, method_name, authorizer_name, authorizers if security: method_definition["security"] = security - def add_tags(self, tags: Dict[str, Intrinsicable[str]]) -> None: + def add_tags(self, tags: dict[str, Intrinsicable[str]]) -> None: """ Adds tags to the OpenApi definition using an ApiGateway extension for tag values. @@ -389,7 +390,7 @@ def add_tags(self, tags: Dict[str, Intrinsicable[str]]) -> None: tag[self._X_APIGW_TAG_VALUE] = value self.tags.append(tag) - def add_endpoint_config(self, disable_execute_api_endpoint: Optional[Intrinsicable[bool]]) -> None: + def add_endpoint_config(self, disable_execute_api_endpoint: Intrinsicable[bool] | None) -> None: """Add endpoint configuration to _X_APIGW_ENDPOINT_CONFIG header in open api definition Following this guide: @@ -418,7 +419,7 @@ def add_endpoint_config(self, disable_execute_api_endpoint: Optional[Intrinsicab self._doc[self._SERVERS] = servers_configurations - def add_cors( # type: ignore[no-untyped-def] # noqa: PLR0913 + def add_cors( # type: ignore[no-untyped-def] self, allow_origins, allow_headers=None, @@ -508,12 +509,10 @@ def add_title(self, title: Intrinsicable[str]) -> None: self.info["title"] = title def has_api_gateway_cors(self) -> bool: - if self._doc.get(self._X_APIGW_CORS): - return True - return False + return bool(self._doc.get(self._X_APIGW_CORS)) @property - def openapi(self) -> Dict[str, Any]: + def openapi(self) -> dict[str, Any]: """ Returns a **copy** of the OpenApi specification as a dictionary. diff --git a/samtranslator/parser/parser.py b/samtranslator/parser/parser.py index 243cedefe6..a032dd45bb 100644 --- a/samtranslator/parser/parser.py +++ b/samtranslator/parser/parser.py @@ -1,5 +1,5 @@ import logging -from typing import Any, Dict +from typing import Any from samtranslator.model.exceptions import ( InvalidDocumentException, @@ -18,7 +18,7 @@ class Parser: def __init__(self) -> None: pass - def parse(self, sam_template: Dict[str, Any], parameter_values: Dict[str, Any], sam_plugins: SamPlugins) -> None: + def parse(self, sam_template: dict[str, Any], parameter_values: dict[str, Any], sam_plugins: SamPlugins) -> None: self._validate(sam_template, parameter_values) # type: ignore[no-untyped-call] sam_plugins.act(LifeCycleEvents.before_transform_template, sam_template) diff --git a/samtranslator/plugins/__init__.py b/samtranslator/plugins/__init__.py index b211c36279..0b71b82f22 100644 --- a/samtranslator/plugins/__init__.py +++ b/samtranslator/plugins/__init__.py @@ -1,7 +1,6 @@ import logging from abc import ABC from enum import Enum -from typing import Optional LOG = logging.getLogger(__name__) @@ -21,9 +20,9 @@ class BasePlugin(ABC): Base class for a NoOp plugin that implements all available hooks """ - _custom_name: Optional[str] + _custom_name: str | None - def __init__(self, name: Optional[str] = None) -> None: + def __init__(self, name: str | None = None) -> None: """ Initialize the plugin with optional given name. diff --git a/samtranslator/plugins/api/implicit_api_plugin.py b/samtranslator/plugins/api/implicit_api_plugin.py index bd6e7e64cc..a878f0ba77 100644 --- a/samtranslator/plugins/api/implicit_api_plugin.py +++ b/samtranslator/plugins/api/implicit_api_plugin.py @@ -1,6 +1,6 @@ import copy from abc import ABCMeta, abstractmethod -from typing import Any, Dict, Generic, Optional, Tuple, Type, TypeVar, Union +from typing import Any, Generic, TypeVar, Union from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model.eventsources.push import Api @@ -14,7 +14,7 @@ from samtranslator.utils.py27hash_fix import Py27Dict from samtranslator.validator.value_validator import sam_expect -T = TypeVar("T", bound=Union[Type[OpenApiEditor], Type[SwaggerEditor]]) +T = TypeVar("T", bound=Union[type[OpenApiEditor], type[SwaggerEditor]]) class ImplicitApiPlugin(BasePlugin, Generic[T], metaclass=ABCMeta): @@ -54,22 +54,22 @@ def __init__(self) -> None: """ super().__init__() - self.existing_implicit_api_resource: Optional[SamResource] = None + self.existing_implicit_api_resource: SamResource | None = None # dict containing condition (or None) for each resource path+method for all APIs. dict format: # {api_id: {path: {method: condition_name_or_None}}} - self.api_conditions: Dict[str, Any] = {} - self.api_deletion_policies: Dict[str, Any] = {} - self.api_update_replace_policies: Dict[str, Any] = {} + self.api_conditions: dict[str, Any] = {} + self.api_deletion_policies: dict[str, Any] = {} + self.api_update_replace_policies: dict[str, Any] = {} @abstractmethod - def _process_api_events( # noqa: PLR0913 + def _process_api_events( self, function: SamResource, - api_events: Dict[str, Dict[str, Any]], + api_events: dict[str, dict[str, Any]], template: SamTemplate, - condition: Optional[str] = None, - deletion_policy: Optional[str] = None, - update_replace_policy: Optional[str] = None, + condition: str | None = None, + deletion_policy: str | None = None, + update_replace_policy: str | None = None, ) -> None: """ Actually process given API events. Iteratively adds the APIs to Swagger JSON in the respective Serverless::Api @@ -88,13 +88,13 @@ def _get_api_definition_from_editor(self, editor): # type: ignore[no-untyped-de """ @abstractmethod - def _generate_implicit_api_resource(self) -> Dict[str, Any]: + def _generate_implicit_api_resource(self) -> dict[str, Any]: """ Helper function implemented by child classes that create a new implicit API resource """ def _add_tags_to_implicit_api_if_necessary( - self, event_properties: Dict[str, Any], resource: SamResource, template: SamTemplate + self, event_properties: dict[str, Any], resource: SamResource, template: SamTemplate ) -> None: """ Decides whether to add tags to the implicit api resource. @@ -224,9 +224,8 @@ def _add_api_to_swagger(self, event_id, event_properties, template): # type: ig # can be found https://github.com/aws/serverless-application-model/blob/develop/tests/translator/output/api_with_any_method_in_swagger.json. # One would argue that, this is unexpected and should actually fail. Instead of suddenly breaking customers in this # position, we added a check to make sure the Plugin run (Http or Rest) is referencing an api of the same type. - is_referencing_http_from_api_event = ( - not template.get(api_id) - or template.get(api_id).type == "AWS::Serverless::HttpApi" + is_referencing_http_from_api_event = not template.get(api_id) or ( + template.get(api_id).type == "AWS::Serverless::HttpApi" and template.get(api_id).type != self.SERVERLESS_API_RESOURCE_TYPE ) @@ -269,7 +268,7 @@ def _add_api_to_swagger(self, event_id, event_properties, template): # type: ig resource.properties["DefinitionBody"] = self._get_api_definition_from_editor(editor) # type: ignore[no-untyped-call] template.set(api_id, resource) - def _get_api_id(self, event_properties: Dict[str, Any]) -> Any: + def _get_api_id(self, event_properties: dict[str, Any]) -> Any: """ Get API logical id from API event properties. @@ -467,7 +466,7 @@ def _maybe_remove_implicit_api(self, template): # type: ignore[no-untyped-def] else: template.delete(self.IMPLICIT_API_LOGICAL_ID) - def _validate_api_event(self, event_id: str, event_properties: Dict[str, Any]) -> Tuple[str, str, str]: + def _validate_api_event(self, event_id: str, event_properties: dict[str, Any]) -> tuple[str, str, str]: """Validate and return api_id, path, method.""" api_id = self._get_api_id(event_properties) path = event_properties.get("Path") @@ -483,14 +482,14 @@ def _validate_api_event(self, event_id: str, event_properties: Dict[str, Any]) - sam_expect(method, event_id, "Method", is_sam_event=True).to_be_a_string(), ) - def _update_resource_attributes_from_api_event( # noqa: PLR0913 + def _update_resource_attributes_from_api_event( self, api_id: str, path: str, method: str, - condition: Optional[str], - deletion_policy: Optional[str], - update_replace_policy: Optional[str], + condition: str | None, + deletion_policy: str | None, + update_replace_policy: str | None, ) -> None: api_dict_condition = self.api_conditions.setdefault(api_id, {}) method_conditions = api_dict_condition.setdefault(path, {}) diff --git a/samtranslator/plugins/api/implicit_http_api_plugin.py b/samtranslator/plugins/api/implicit_http_api_plugin.py index 292ded99be..9f3a6d3821 100644 --- a/samtranslator/plugins/api/implicit_http_api_plugin.py +++ b/samtranslator/plugins/api/implicit_http_api_plugin.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional, Type, cast +from typing import Any, cast from samtranslator.model.intrinsics import make_conditional from samtranslator.plugins.api.implicit_api_plugin import ImplicitApiPlugin @@ -8,7 +8,7 @@ from samtranslator.validator.value_validator import sam_expect -class ImplicitHttpApiPlugin(ImplicitApiPlugin[Type[OpenApiEditor]]): +class ImplicitHttpApiPlugin(ImplicitApiPlugin[type[OpenApiEditor]]): """ This plugin provides Implicit Http API shorthand syntax in the SAM Spec. @@ -33,14 +33,14 @@ class ImplicitHttpApiPlugin(ImplicitApiPlugin[Type[OpenApiEditor]]): SERVERLESS_API_RESOURCE_TYPE = SamResourceType.HttpApi.value EDITOR_CLASS = OpenApiEditor - def _process_api_events( # noqa: PLR0913 + def _process_api_events( self, function: SamResource, - api_events: Dict[str, Dict[str, Any]], + api_events: dict[str, dict[str, Any]], template: SamTemplate, - condition: Optional[str] = None, - deletion_policy: Optional[str] = None, - update_replace_policy: Optional[str] = None, + condition: str | None = None, + deletion_policy: str | None = None, + update_replace_policy: str | None = None, ) -> None: """ Actually process given HTTP API events. Iteratively adds the APIs to OpenApi JSON in the respective @@ -86,20 +86,20 @@ def _process_api_events( # noqa: PLR0913 # We could have made changes to the Events structure. Write it back to function function.properties["Events"].update(api_events) - def _generate_implicit_api_resource(self) -> Dict[str, Any]: + def _generate_implicit_api_resource(self) -> dict[str, Any]: """ Uses the implicit API in this file to generate an Implicit API resource """ return ImplicitHttpApiResource().to_dict() - def _get_api_definition_from_editor(self, editor: OpenApiEditor) -> Dict[str, Any]: + def _get_api_definition_from_editor(self, editor: OpenApiEditor) -> dict[str, Any]: """ Helper function to return the OAS definition from the editor """ return editor.openapi def _add_route_settings_to_api( - self, event_id: str, event_properties: Dict[str, Any], template: SamTemplate, condition: Optional[str] + self, event_id: str, event_properties: dict[str, Any], template: SamTemplate, condition: str | None ) -> None: """ Adds the RouteSettings for this path/method from the given event to the RouteSettings configuration diff --git a/samtranslator/plugins/api/implicit_rest_api_plugin.py b/samtranslator/plugins/api/implicit_rest_api_plugin.py index aae5e0e5b5..97c5372f00 100644 --- a/samtranslator/plugins/api/implicit_rest_api_plugin.py +++ b/samtranslator/plugins/api/implicit_rest_api_plugin.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional, Type +from typing import Any from samtranslator.plugins.api.implicit_api_plugin import ImplicitApiPlugin from samtranslator.public.sdk.resource import SamResource, SamResourceType @@ -7,7 +7,7 @@ from samtranslator.validator.value_validator import sam_expect -class ImplicitRestApiPlugin(ImplicitApiPlugin[Type[SwaggerEditor]]): +class ImplicitRestApiPlugin(ImplicitApiPlugin[type[SwaggerEditor]]): """ This plugin provides Implicit API shorthand syntax in the SAM Spec. https://github.com/aws/serverless-application-model/blob/master/versions/2016-10-31.md#api @@ -35,14 +35,14 @@ class ImplicitRestApiPlugin(ImplicitApiPlugin[Type[SwaggerEditor]]): SERVERLESS_API_RESOURCE_TYPE = SamResourceType.Api.value EDITOR_CLASS = SwaggerEditor - def _process_api_events( # noqa: PLR0913 + def _process_api_events( self, function: SamResource, - api_events: Dict[str, Dict[str, Any]], + api_events: dict[str, dict[str, Any]], template: SamTemplate, - condition: Optional[str] = None, - deletion_policy: Optional[str] = None, - update_replace_policy: Optional[str] = None, + condition: str | None = None, + deletion_policy: str | None = None, + update_replace_policy: str | None = None, ) -> None: """ Actually process given API events. Iteratively adds the APIs to Swagger JSON in the respective Serverless::Api @@ -77,13 +77,13 @@ def _process_api_events( # noqa: PLR0913 # We could have made changes to the Events structure. Write it back to function function.properties["Events"].update(api_events) - def _generate_implicit_api_resource(self) -> Dict[str, Any]: + def _generate_implicit_api_resource(self) -> dict[str, Any]: """ Uses the implicit API in this file to generate an Implicit API resource """ return ImplicitApiResource().to_dict() - def _get_api_definition_from_editor(self, editor: SwaggerEditor) -> Dict[str, Any]: + def _get_api_definition_from_editor(self, editor: SwaggerEditor) -> dict[str, Any]: """ Helper function to return the OAS definition from the editor """ diff --git a/samtranslator/plugins/application/serverless_app_plugin.py b/samtranslator/plugins/application/serverless_app_plugin.py index 02d70d9f71..fecbb5f39f 100644 --- a/samtranslator/plugins/application/serverless_app_plugin.py +++ b/samtranslator/plugins/application/serverless_app_plugin.py @@ -2,8 +2,9 @@ import json import logging import re +from collections.abc import Callable from time import sleep -from typing import Any, Callable, Dict, List, Optional, Tuple +from typing import Any import boto3 from botocore.client import BaseClient @@ -54,11 +55,11 @@ class ServerlessAppPlugin(BasePlugin): def __init__( self, - sar_client: Optional[BaseClient] = None, + sar_client: BaseClient | None = None, wait_for_template_active_status: bool = False, validate_only: bool = False, - parameters: Optional[Dict[str, Any]] = None, - sar_client_creator: Optional[Callable[[], BaseClient]] = None, + parameters: dict[str, Any] | None = None, + sar_client_creator: Callable[[], BaseClient] | None = None, ) -> None: """ Initialize the plugin. @@ -73,8 +74,8 @@ def __init__( super().__init__() if parameters is None: parameters = {} - self._applications: Dict[Tuple[str, str], Any] = {} - self._in_progress_templates: List[Tuple[str, str]] = [] + self._applications: dict[tuple[str, str], Any] = {} + self._in_progress_templates: list[tuple[str, str]] = [] self.__sar_client = sar_client self._sar_client_creator = sar_client_creator self._wait_for_template_active_status = wait_for_template_active_status @@ -100,7 +101,7 @@ def _sar_client(self) -> BaseClient: return self.__sar_client @staticmethod - def _make_app_key(app_id: Any, semver: Any) -> Tuple[str, str]: + def _make_app_key(app_id: Any, semver: Any) -> tuple[str, str]: """Generate a key that is always hashable.""" return json.dumps(app_id, default=str), json.dumps(semver, default=str) @@ -399,7 +400,7 @@ def on_after_transform_template(self, template): # type: ignore[no-untyped-def] def _get_sleep_time_sec(self) -> int: return self.SLEEP_TIME_SECONDS - def _is_template_active(self, response: Dict[str, Any], application_id: str, template_id: str) -> bool: + def _is_template_active(self, response: dict[str, Any], application_id: str, template_id: str) -> bool: """ Checks the response from a SAR service call; returns True if the template is active, throws an exception if the request expired and returns False in all other cases. diff --git a/samtranslator/plugins/globals/globals.py b/samtranslator/plugins/globals/globals.py index 9defaeaf98..004adab351 100644 --- a/samtranslator/plugins/globals/globals.py +++ b/samtranslator/plugins/globals/globals.py @@ -1,5 +1,5 @@ import copy -from typing import Any, Dict, List, Optional, Union +from typing import Any, Union from samtranslator.model.exceptions import ExceptionWithMessage, InvalidResourceAttributeTypeException from samtranslator.public.intrinsics import is_intrinsics @@ -116,12 +116,12 @@ class Globals: ], } # unreleased_properties *must be* part of supported_properties too - unreleased_properties: Dict[str, List[str]] = { + unreleased_properties: dict[str, list[str]] = { SamResourceType.Function.value: [], } - unreleased_resource_types: List[str] = [] + unreleased_resource_types: list[str] = [] - def __init__(self, template: Dict[str, Any]) -> None: + def __init__(self, template: dict[str, Any]) -> None: """ Constructs an instance of this object @@ -135,13 +135,13 @@ def __init__(self, template: Dict[str, Any]) -> None: # Sort the names for stability in list ordering self.supported_resource_section_names.sort() - self.template_globals: Dict[str, GlobalProperties] = {} + self.template_globals: dict[str, GlobalProperties] = {} if self._KEYWORD in template: self.template_globals = self._parse(template[self._KEYWORD]) # type: ignore[no-untyped-call] def get_template_globals( - self, logical_id: str, resource_type: str, ignore_globals: Optional[Union[str, List[str]]] + self, logical_id: str, resource_type: str, ignore_globals: Union[str, list[str]] | None ) -> "GlobalProperties": """ Get template globals but remove globals based on IgnoreGlobals attribute. @@ -182,9 +182,9 @@ def get_template_globals( def merge( self, resource_type: str, - resource_properties: Dict[str, Any], + resource_properties: dict[str, Any], logical_id: str = "", - ignore_globals: Optional[Union[str, List[str]]] = None, + ignore_globals: Union[str, list[str]] | None = None, ) -> Any: """ Adds global properties to the resource, if necessary. This method is a no-op if there are no global properties @@ -204,7 +204,7 @@ def merge( return global_props.merge(resource_properties) # type: ignore[no-untyped-call] @classmethod - def del_section(cls, template: Dict[str, Any]) -> None: + def del_section(cls, template: dict[str, Any]) -> None: """ Helper method to delete the Globals section altogether from the template @@ -216,7 +216,7 @@ def del_section(cls, template: Dict[str, Any]) -> None: del template[cls._KEYWORD] @classmethod - def fix_openapi_definitions(cls, template: Dict[str, Any]) -> None: + def fix_openapi_definitions(cls, template: dict[str, Any]) -> None: """ Helper method to postprocess the resources to make sure the swagger doc version matches the one specified on the resource with flag OpenApiVersion. @@ -459,7 +459,7 @@ def _do_merge(self, global_value, local_value): # type: ignore[no-untyped-def] def _merge_lists(self, global_list, local_list): # type: ignore[no-untyped-def] """ - Merges the global list with the local list. List merging is simply a concatenation = global + local + Merges the global list with the local list. list merging is simply a concatenation = global + local :param global_list: Global value list :param local_list: Local value list diff --git a/samtranslator/plugins/globals/globals_plugin.py b/samtranslator/plugins/globals/globals_plugin.py index bb1c1028ee..b46c4c9762 100644 --- a/samtranslator/plugins/globals/globals_plugin.py +++ b/samtranslator/plugins/globals/globals_plugin.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model.exceptions import InvalidResourceAttributeTypeException @@ -16,7 +16,7 @@ class GlobalsPlugin(BasePlugin): """ @cw_timer(prefix="Plugin-Globals") - def on_before_transform_template(self, template_dict: Dict[str, Any]) -> None: + def on_before_transform_template(self, template_dict: dict[str, Any]) -> None: """ Hook method that runs before a template gets transformed. In this method, we parse and process Globals section from the template (if present). diff --git a/samtranslator/plugins/sam_plugins.py b/samtranslator/plugins/sam_plugins.py index 54298fab1e..914ba5e572 100644 --- a/samtranslator/plugins/sam_plugins.py +++ b/samtranslator/plugins/sam_plugins.py @@ -1,5 +1,5 @@ import logging -from typing import Any, List, Optional, Union +from typing import Any, Union from samtranslator.model.exceptions import InvalidDocumentException, InvalidResourceException, InvalidTemplateException from samtranslator.plugins import BasePlugin, LifeCycleEvents @@ -47,13 +47,13 @@ class SamPlugins: set by the plugin. SAM translator will convert this into a nice error message and display to the user. """ - def __init__(self, initial_plugins: Optional[Union[BasePlugin, List[BasePlugin]]] = None) -> None: + def __init__(self, initial_plugins: Union[BasePlugin, list[BasePlugin]] | None = None) -> None: """ Initialize the plugins class with an optional list of plugins - :param BasePlugin or list initial_plugins: Single plugin or a List of plugins to initialize with + :param BasePlugin or list initial_plugins: Single plugin or a list of plugins to initialize with """ - self._plugins: List[BasePlugin] = [] + self._plugins: list[BasePlugin] = [] if initial_plugins is None: initial_plugins = [] diff --git a/samtranslator/policy_template_processor/processor.py b/samtranslator/policy_template_processor/processor.py index a749a552a2..e894249947 100644 --- a/samtranslator/policy_template_processor/processor.py +++ b/samtranslator/policy_template_processor/processor.py @@ -1,6 +1,6 @@ import json from pathlib import Path -from typing import Any, Dict, Optional +from typing import Any import jsonschema from jsonschema.exceptions import ValidationError @@ -51,7 +51,7 @@ class PolicyTemplatesProcessor: # ./policy_templates.json DEFAULT_POLICY_TEMPLATES_FILE = policy_templates_data.POLICY_TEMPLATES_FILE - def __init__(self, policy_templates_dict: Dict[str, Any], schema: Optional[Dict[str, Any]] = None) -> None: + def __init__(self, policy_templates_dict: dict[str, Any], schema: dict[str, Any] | None = None) -> None: """ Initialize the class @@ -103,9 +103,7 @@ def convert(self, template_name: str, parameter_values: str) -> Any: return template.to_statement(parameter_values) @staticmethod - def _is_valid_templates_dict( - policy_templates_dict: Dict[Any, Any], schema: Optional[Dict[Any, Any]] = None - ) -> bool: + def _is_valid_templates_dict(policy_templates_dict: dict[Any, Any], schema: dict[Any, Any] | None = None) -> bool: """ Is this a valid policy template dictionary diff --git a/samtranslator/public/exceptions.py b/samtranslator/public/exceptions.py index 54a870b1d2..7371822f2b 100644 --- a/samtranslator/public/exceptions.py +++ b/samtranslator/public/exceptions.py @@ -1,3 +1,3 @@ -__all__ = ["InvalidResourceException", "InvalidDocumentException", "InvalidEventException"] +__all__ = ["InvalidDocumentException", "InvalidEventException", "InvalidResourceException"] from samtranslator.model.exceptions import InvalidDocumentException, InvalidEventException, InvalidResourceException diff --git a/samtranslator/public/models.py b/samtranslator/public/models.py index 7553aa01e3..6eddf067bf 100644 --- a/samtranslator/public/models.py +++ b/samtranslator/public/models.py @@ -1,5 +1,3 @@ -from typing import List - from samtranslator.model.resource_policies import PolicyTypes, ResourcePolicies -__all__: List[str] = ["PolicyTypes", "ResourcePolicies"] +__all__: list[str] = ["PolicyTypes", "ResourcePolicies"] diff --git a/samtranslator/public/parser.py b/samtranslator/public/parser.py index dbb8c10043..96f3ffdad5 100644 --- a/samtranslator/public/parser.py +++ b/samtranslator/public/parser.py @@ -1,5 +1,3 @@ -from typing import List - from samtranslator.parser.parser import Parser -__all__: List[str] = ["Parser"] +__all__: list[str] = ["Parser"] diff --git a/samtranslator/public/sdk/parameter.py b/samtranslator/public/sdk/parameter.py index 8f397a222e..49a89da8ce 100644 --- a/samtranslator/public/sdk/parameter.py +++ b/samtranslator/public/sdk/parameter.py @@ -1,5 +1,3 @@ -from typing import List - from samtranslator.sdk.parameter import SamParameterValues -__all__: List[str] = ["SamParameterValues"] +__all__: list[str] = ["SamParameterValues"] diff --git a/samtranslator/public/translator.py b/samtranslator/public/translator.py index 1e98511982..775ea9dbeb 100644 --- a/samtranslator/public/translator.py +++ b/samtranslator/public/translator.py @@ -2,7 +2,7 @@ # This is essentially our Public API # -__all__ = ["Translator", "ManagedPolicyLoader"] +__all__ = ["ManagedPolicyLoader", "Translator"] from samtranslator.translator.managed_policy_translator import ManagedPolicyLoader from samtranslator.translator.translator import Translator diff --git a/samtranslator/region_configuration.py b/samtranslator/region_configuration.py index 333ca3864f..45b3675348 100644 --- a/samtranslator/region_configuration.py +++ b/samtranslator/region_configuration.py @@ -18,9 +18,7 @@ def is_apigw_edge_configuration_supported(cls) -> bool: :return: True, if API Gateway does not support Edge configuration """ partition = ArnGenerator.get_partition_name() - if partition.startswith("aws-iso") or partition in ["aws-us-gov", "aws-cn", "aws-eusc"]: - return False - return True + return not (partition.startswith("aws-iso") or partition in ["aws-us-gov", "aws-cn", "aws-eusc"]) @classmethod def is_service_supported(cls, service, region=None): # type: ignore[no-untyped-def] diff --git a/samtranslator/schema/schema.json b/samtranslator/schema/schema.json index cfbcf14ca0..098b267690 100644 --- a/samtranslator/schema/schema.json +++ b/samtranslator/schema/schema.json @@ -357042,10 +357042,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, @@ -357064,10 +357064,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, @@ -357086,10 +357086,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, @@ -358961,10 +358961,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, @@ -359289,10 +359289,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, @@ -359311,10 +359311,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, diff --git a/samtranslator/sdk/parameter.py b/samtranslator/sdk/parameter.py index 5e0218fc59..8771694195 100644 --- a/samtranslator/sdk/parameter.py +++ b/samtranslator/sdk/parameter.py @@ -1,5 +1,5 @@ import copy -from typing import Any, Dict, Optional +from typing import Any import boto3 from boto3 import Session @@ -12,7 +12,7 @@ class SamParameterValues: Class representing SAM parameter values. """ - def __init__(self, parameter_values: Dict[Any, Any]) -> None: + def __init__(self, parameter_values: dict[Any, Any]) -> None: """ Initialize the object given the parameter values as a dictionary @@ -21,7 +21,7 @@ def __init__(self, parameter_values: Dict[Any, Any]) -> None: self.parameter_values = copy.deepcopy(parameter_values) - def add_default_parameter_values(self, sam_template: Dict[str, Any]) -> Any: + def add_default_parameter_values(self, sam_template: dict[str, Any]) -> Any: """ Method to read default values for template parameters and merge with user supplied values. @@ -65,7 +65,7 @@ def add_default_parameter_values(self, sam_template: Dict[str, Any]) -> Any: return None - def add_pseudo_parameter_values(self, session: Optional[Session] = None) -> None: + def add_pseudo_parameter_values(self, session: Session | None = None) -> None: """ Add pseudo parameter values :return: parameter values that have pseudo parameter in it diff --git a/samtranslator/sdk/resource.py b/samtranslator/sdk/resource.py index c0106b0b64..d2b0a24a7f 100644 --- a/samtranslator/sdk/resource.py +++ b/samtranslator/sdk/resource.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Any, Dict, List, Optional, Union +from typing import Any, Union from samtranslator.model.exceptions import InvalidDocumentException, InvalidTemplateException from samtranslator.model.types import IS_STR @@ -13,9 +13,9 @@ class SamResource: """ type = None - properties: Dict[str, Any] = {} # TODO: Replace `Any` with something more specific + properties: dict[str, Any] = {} # TODO: Replace `Any` with something more specific - def __init__(self, resource_dict: Dict[str, Any]) -> None: + def __init__(self, resource_dict: dict[str, Any]) -> None: """ Initialize the object given the resource as a dictionary @@ -27,7 +27,7 @@ def __init__(self, resource_dict: Dict[str, Any]) -> None: self.condition = resource_dict.get("Condition") self.deletion_policy = resource_dict.get("DeletionPolicy") self.update_replace_policy = resource_dict.get("UpdateReplacePolicy") - self.ignore_globals: Optional[Union[str, List[str]]] = resource_dict.get("IgnoreGlobals") + self.ignore_globals: Union[str, list[str]] | None = resource_dict.get("IgnoreGlobals") # Properties is *not* required. Ex: SimpleTable resource has no required properties self.properties = resource_dict.get("Properties", {}) @@ -47,7 +47,7 @@ def valid(self) -> bool: # TODO: should we raise exception if `self.type` is not a string? return isinstance(self.type, str) and SamResourceType.has_value(self.type) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: if self.valid(): # Touch a resource dictionary ONLY if it is valid # Modify only Type & Properties section to preserve CloudFormation properties like DependsOn, Conditions etc diff --git a/samtranslator/sdk/template.py b/samtranslator/sdk/template.py index 0b90bc97de..812c6a83ab 100644 --- a/samtranslator/sdk/template.py +++ b/samtranslator/sdk/template.py @@ -2,7 +2,8 @@ Classes representing SAM template and resources. """ -from typing import Any, Dict, Iterator, Optional, Set, Tuple, Union +from collections.abc import Iterator +from typing import Any, Union from samtranslator.sdk.resource import SamResource @@ -12,7 +13,7 @@ class SamTemplate: Class representing the SAM template """ - def __init__(self, template_dict: Dict[str, Any]) -> None: + def __init__(self, template_dict: dict[str, Any]) -> None: """ Initialize with a template dictionary, that contains "Resources" dictionary @@ -21,12 +22,12 @@ def __init__(self, template_dict: Dict[str, Any]) -> None: self.template_dict = template_dict self.resources = template_dict["Resources"] - def iterate(self, resource_types: Optional[Set[str]] = None) -> Iterator[Tuple[str, SamResource]]: + def iterate(self, resource_types: set[str] | None = None) -> Iterator[tuple[str, SamResource]]: """ Iterate over all resources within the SAM template, optionally filtering by type :param set resource_types: Optional types to filter the resources by - :yields (string, SamResource): Tuple containing LogicalId and the resource + :yields (string, SamResource): tuple containing LogicalId and the resource """ if resource_types is None: resource_types = set() @@ -39,7 +40,7 @@ def iterate(self, resource_types: Optional[Set[str]] = None) -> Iterator[Tuple[s if needs_filter: yield logicalId, resource - def set(self, logical_id: str, resource: Union[SamResource, Dict[str, Any]]) -> None: + def set(self, logical_id: str, resource: Union[SamResource, dict[str, Any]]) -> None: """ Adds the resource to dictionary with given logical Id. It will overwrite, if the logical_id is already used. @@ -53,7 +54,7 @@ def set(self, logical_id: str, resource: Union[SamResource, Dict[str, Any]]) -> self.resources[logical_id] = resource_dict - def get_globals(self) -> Dict[str, Any]: + def get_globals(self) -> dict[str, Any]: """ Gets the global section of the template @@ -61,7 +62,7 @@ def get_globals(self) -> Dict[str, Any]: """ return self.template_dict.get("Globals") or {} - def get(self, logical_id: str) -> Optional[SamResource]: + def get(self, logical_id: str) -> SamResource | None: """ Gets the resource at the given logical_id if present @@ -83,7 +84,7 @@ def delete(self, logicalId): # type: ignore[no-untyped-def] if logicalId in self.resources: del self.resources[logicalId] - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """ Returns the template as a dictionary diff --git a/samtranslator/swagger/swagger.py b/samtranslator/swagger/swagger.py index 2c4c747c91..3f0b3c169b 100644 --- a/samtranslator/swagger/swagger.py +++ b/samtranslator/swagger/swagger.py @@ -1,6 +1,7 @@ import copy import re -from typing import Any, Callable, Dict, Optional, TypeVar +from collections.abc import Callable +from typing import Any, TypeVar from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model.apigateway import ApiGatewayAuthorizer @@ -50,9 +51,9 @@ class SwaggerEditor(BaseEditor): _DISABLE_EXECUTE_API_ENDPOINT = "disableExecuteApiEndpoint" # Attributes: - _doc: Dict[str, Any] + _doc: dict[str, Any] - def __init__(self, doc: Optional[Dict[str, Any]]) -> None: + def __init__(self, doc: dict[str, Any] | None) -> None: """ Initialize the class with a swagger dictionary. This class creates a copy of the Swagger and performs all modifications on this copy. @@ -122,10 +123,10 @@ def add_lambda_integration( # noqa: PLR0913 path: str, method: str, integration_uri: str, - method_auth_config: Dict[str, Any], - api_auth_config: Dict[str, Any], - condition: Optional[str] = None, - invoke_mode: Optional[Any] = None, + method_auth_config: dict[str, Any], + api_auth_config: dict[str, Any], + condition: str | None = None, + invoke_mode: Any | None = None, ) -> None: """ Adds aws_proxy APIGW integration to the given path+method. @@ -155,10 +156,8 @@ def add_lambda_integration( # noqa: PLR0913 if invoke_mode == "RESPONSE_STREAM": path_item[method][self._X_APIGW_INTEGRATION]["responseTransferMode"] = "STREAM" - if ( - method_auth_config.get("Authorizer") == "AWS_IAM" - or api_auth_config.get("DefaultAuthorizer") == "AWS_IAM" - and not method_auth_config + if method_auth_config.get("Authorizer") == "AWS_IAM" or ( + api_auth_config.get("DefaultAuthorizer") == "AWS_IAM" and not method_auth_config ): method_invoke_role = method_auth_config.get("InvokeRole") if not method_invoke_role and "InvokeRole" in method_auth_config: @@ -179,7 +178,7 @@ def add_lambda_integration( # noqa: PLR0913 if condition: path_item[method] = make_conditional(condition, path_item[method]) - def add_state_machine_integration( # type: ignore[no-untyped-def] # noqa: PLR0913 + def add_state_machine_integration( # type: ignore[no-untyped-def] self, path, method, @@ -275,7 +274,7 @@ def iter_on_all_methods_for_path(self, path_name, skip_methods_without_apigw_int normalized_method_name = self._normalize_method_name(method_name) yield normalized_method_name, method_definition - def add_cors( # type: ignore[no-untyped-def] # noqa: PLR0913 + def add_cors( # type: ignore[no-untyped-def] self, path, allowed_origins, allowed_headers=None, allowed_methods=None, max_age=None, allow_credentials=None ): """ @@ -436,7 +435,7 @@ def _options_method_response_for_cors( # type: ignore[no-untyped-def] to_return["responses"]["200"]["headers"] = response_headers return to_return - def _make_cors_allowed_methods_for_path_item(self, path_item: Dict[str, Any]) -> str: + def _make_cors_allowed_methods_for_path_item(self, path_item: dict[str, Any]) -> str: """ Creates the value for Access-Control-Allow-Methods header for given path item. All HTTP methods defined for this path item will be included in the result. If the path item contains "ANY" method, then *all available* HTTP methods will @@ -529,7 +528,7 @@ def set_path_default_authorizer( # noqa: PLR0912 self, path: str, default_authorizer: str, - authorizers: Dict[str, ApiGatewayAuthorizer], + authorizers: dict[str, ApiGatewayAuthorizer], add_default_auth_to_preflight: bool = True, ) -> None: """ @@ -681,7 +680,7 @@ def set_path_default_apikey_required(self, path: str, required_options_api_key: if security != existing_security: method_definition["security"] = security - def add_auth_to_method(self, path: str, method_name: str, auth: Dict[str, Any], api: Dict[str, Any]) -> None: + def add_auth_to_method(self, path: str, method_name: str, auth: dict[str, Any], api: dict[str, Any]) -> None: """ Adds auth settings for this path/method. Auth settings currently consist of Authorizers and ApiKeyRequired but this method will eventually include setting other auth settings such as Resource Policy, etc. @@ -885,7 +884,7 @@ def add_models(self, models): # type: ignore[no-untyped-def] self.definitions[model_name.lower()] = schema - def add_resource_policy(self, resource_policy: Optional[Dict[str, Any]], path: str, stage: PassThrough) -> None: + def add_resource_policy(self, resource_policy: dict[str, Any] | None, path: str, stage: PassThrough) -> None: """ Add resource policy definition to Swagger. @@ -1071,7 +1070,7 @@ def _add_ip_resource_policy_for_method(self, ip_list, conditional, resource_list self.resource_policy["Statement"] = statement def _add_vpc_resource_policy_for_method( # noqa: PLR0912 - self, endpoint_dict: Dict[str, Any], conditional: str, resource_list: PassThrough + self, endpoint_dict: dict[str, Any], conditional: str, resource_list: PassThrough ) -> None: """ This method generates a policy statement to grant/deny specific VPC/VPCE access to the API method and @@ -1198,7 +1197,7 @@ def add_request_parameters_to_method(self, path, method_name, request_parameters method_definition["parameters"] = existing_parameters @property - def swagger(self) -> Dict[str, Any]: + def swagger(self) -> dict[str, Any]: """ Returns a **copy** of the Swagger document as a dictionary. @@ -1298,7 +1297,4 @@ def _validate_list_property_is_resolved(property_list): # type: ignore[no-untyp :return bool: True if the property_list is all of type string otherwise False """ - if property_list is not None and not all(isinstance(x, str) for x in property_list): - return False - - return True + return not (property_list is not None and not all(isinstance(x, str) for x in property_list)) diff --git a/samtranslator/translator/arn_generator.py b/samtranslator/translator/arn_generator.py index 3c67484917..1a39819af4 100644 --- a/samtranslator/translator/arn_generator.py +++ b/samtranslator/translator/arn_generator.py @@ -1,5 +1,4 @@ from functools import lru_cache -from typing import Optional import boto3 @@ -34,7 +33,7 @@ def _region_to_partition(region: str) -> str: class ArnGenerator: - BOTO_SESSION_REGION_NAME: Optional[str] = None + BOTO_SESSION_REGION_NAME: str | None = None @classmethod def generate_arn( @@ -43,7 +42,7 @@ def generate_arn( service: str, resource: str, include_account_id: bool = True, - region: Optional[str] = None, + region: str | None = None, ) -> str: """Generate AWS ARN. @@ -99,7 +98,7 @@ def generate_aws_managed_policy_arn(cls, policy_name: str) -> str: return f"arn:{ArnGenerator.get_partition_name()}:iam::aws:policy/{policy_name}" @classmethod - def get_partition_name(cls, region: Optional[str] = None) -> str: + def get_partition_name(cls, region: str | None = None) -> str: """ Gets the name of the partition given the region name. If region name is not provided, this method will use Boto3 to get name of the region where this code is running. diff --git a/samtranslator/translator/logical_id_generator.py b/samtranslator/translator/logical_id_generator.py index 2c9a755f9f..e6128b5af6 100644 --- a/samtranslator/translator/logical_id_generator.py +++ b/samtranslator/translator/logical_id_generator.py @@ -1,6 +1,6 @@ import hashlib import json -from typing import Any, Optional +from typing import Any class LogicalIdGenerator: @@ -8,7 +8,7 @@ class LogicalIdGenerator: # given by this class HASH_LENGTH = 10 - def __init__(self, prefix: str, data_obj: Optional[Any] = None, data_hash: Optional[str] = None) -> None: + def __init__(self, prefix: str, data_obj: Any | None = None, data_hash: str | None = None) -> None: """ Generate logical IDs for resources that are stable, deterministic and platform independent diff --git a/samtranslator/translator/managed_policy_translator.py b/samtranslator/translator/managed_policy_translator.py index 1303a8ff20..87bddf727a 100644 --- a/samtranslator/translator/managed_policy_translator.py +++ b/samtranslator/translator/managed_policy_translator.py @@ -1,5 +1,5 @@ import logging -from typing import Dict, Optional, cast +from typing import cast from botocore.client import BaseClient @@ -11,7 +11,7 @@ class ManagedPolicyLoader: def __init__(self, iam_client: BaseClient) -> None: self._iam_client = iam_client - self._policy_map: Optional[Dict[str, str]] = None + self._policy_map: dict[str, str] | None = None self.max_items = 1000 @cw_timer(prefix="External", name="IAM") @@ -25,7 +25,7 @@ def _load_policies_from_iam(self) -> None: # Note(jfuss): boto3 PaginationConfig MaxItems does not control the number of items returned from the API # call. This is actually controlled by PageSize. page_iterator = paginator.paginate(Scope="AWS", PaginationConfig={"PageSize": self.max_items}) - name_to_arn_map: Dict[str, str] = {} + name_to_arn_map: dict[str, str] = {} for page in page_iterator: name_to_arn_map.update((x["PolicyName"], x["Arn"]) for x in page["Policies"]) @@ -33,8 +33,8 @@ def _load_policies_from_iam(self) -> None: LOG.info("Finished loading policies from IAM.") self._policy_map = name_to_arn_map - def load(self) -> Dict[str, str]: + def load(self) -> dict[str, str]: if self._policy_map is None: self._load_policies_from_iam() # mypy doesn't realize that function above assigns non-None value - return cast(Dict[str, str], self._policy_map) + return cast(dict[str, str], self._policy_map) diff --git a/samtranslator/translator/transform.py b/samtranslator/translator/transform.py index 34ec50709f..65871538fa 100644 --- a/samtranslator/translator/transform.py +++ b/samtranslator/translator/transform.py @@ -1,5 +1,5 @@ -from functools import lru_cache -from typing import Any, Dict, Optional +from functools import cache +from typing import Any from samtranslator.feature_toggle.feature_toggle import FeatureToggle from samtranslator.parser.parser import Parser @@ -9,12 +9,12 @@ def transform( - input_fragment: Dict[str, Any], - parameter_values: Dict[str, Any], + input_fragment: dict[str, Any], + parameter_values: dict[str, Any], managed_policy_loader: ManagedPolicyLoader, - feature_toggle: Optional[FeatureToggle] = None, - passthrough_metadata: Optional[bool] = False, -) -> Dict[str, Any]: + feature_toggle: FeatureToggle | None = None, + passthrough_metadata: bool | None = False, +) -> dict[str, Any]: """Translates the SAM manifest provided in the and returns the translation to CloudFormation. :param dict input_fragment: the SAM template to transform @@ -30,8 +30,8 @@ def transform( sam_parser, ) - @lru_cache(maxsize=None) - def get_managed_policy_map() -> Dict[str, str]: + @cache + def get_managed_policy_map() -> dict[str, str]: return managed_policy_loader.load() transformed = translator.translate( diff --git a/samtranslator/translator/translator.py b/samtranslator/translator/translator.py index b4a0528973..66e0a1bf40 100644 --- a/samtranslator/translator/translator.py +++ b/samtranslator/translator/translator.py @@ -1,5 +1,5 @@ import copy -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple +from typing import TYPE_CHECKING, Any from boto3 import Session @@ -47,33 +47,33 @@ class Translator: def __init__( self, - managed_policy_map: Optional[Dict[str, str]], + managed_policy_map: dict[str, str] | None, sam_parser: Parser, - plugins: Optional[List[BasePlugin]] = None, - boto_session: Optional[Session] = None, - metrics: Optional[Metrics] = None, + plugins: list[BasePlugin] | None = None, + boto_session: Session | None = None, + metrics: Metrics | None = None, ) -> None: """ :param dict managed_policy_map: Map of managed policy names to the ARNs :param sam_parser: Instance of a SAM Parser - :param list of samtranslator.plugins.BasePlugin plugins: List of plugins to be installed in the translator, + :param list of samtranslator.plugins.BasePlugin plugins: list of plugins to be installed in the translator, in addition to the default ones. """ self.managed_policy_map = managed_policy_map self.plugins = plugins self.sam_parser = sam_parser - self.feature_toggle: Optional[FeatureToggle] = None + self.feature_toggle: FeatureToggle | None = None self.boto_session = boto_session self.metrics = metrics if metrics else Metrics("ServerlessTransform", DummyMetricsPublisher()) MetricsMethodWrapperSingleton.set_instance(self.metrics) - self.document_errors: List[ExceptionWithMessage] = [] + self.document_errors: list[ExceptionWithMessage] = [] if self.boto_session: ArnGenerator.BOTO_SESSION_REGION_NAME = self.boto_session.region_name def _get_function_names( - self, resource_dict: Dict[str, Any], intrinsics_resolver: IntrinsicsResolver - ) -> Dict[str, str]: + self, resource_dict: dict[str, Any], intrinsics_resolver: IntrinsicsResolver + ) -> dict[str, str]: """ :param resource_dict: AWS::Serverless::Function resource is provided as input :param intrinsics_resolver: to resolve intrinsics for function_name @@ -104,12 +104,12 @@ def _get_function_names( def translate( # noqa: PLR0912, PLR0915 self, - sam_template: Dict[str, Any], - parameter_values: Dict[str, Any], - feature_toggle: Optional[FeatureToggle] = None, - passthrough_metadata: Optional[bool] = False, - get_managed_policy_map: Optional[GetManagedPolicyMap] = None, - ) -> Dict[str, Any]: + sam_template: dict[str, Any], + parameter_values: dict[str, Any], + feature_toggle: FeatureToggle | None = None, + passthrough_metadata: bool | None = False, + get_managed_policy_map: GetManagedPolicyMap | None = None, + ) -> dict[str, Any]: """Loads the SAM resources from the given SAM manifest, replaces them with their corresponding CloudFormation resources, and returns the resulting CloudFormation template. @@ -127,7 +127,7 @@ def translate( # noqa: PLR0912, PLR0915 self.feature_toggle = feature_toggle or FeatureToggle( FeatureToggleDefaultConfigProvider(), stage=None, account_id=None, region=None ) - self.function_names: Dict[Any, Any] = {} + self.function_names: dict[Any, Any] = {} self.redeploy_restapi_parameters = {} sam_parameter_values = SamParameterValues(parameter_values) sam_parameter_values.add_default_parameter_values(sam_template) @@ -161,7 +161,7 @@ def translate( # noqa: PLR0912, PLR0915 supported_resource_refs = SupportedResourceReferences() shared_api_usage_plan = SharedApiUsagePlan() changed_logical_ids = {} - route53_record_set_groups: Dict[Any, Any] = {} + route53_record_set_groups: dict[Any, Any] = {} for logical_id, resource_dict in self._get_resources_to_iterate(sam_template, macro_resolver): try: macro = macro_resolver.resolve_resource_type(resource_dict).from_dict( @@ -248,8 +248,8 @@ def translate( # noqa: PLR0912, PLR0915 # private methods def _get_resources_to_iterate( - self, sam_template: Dict[str, Any], macro_resolver: ResourceTypeResolver - ) -> List[Tuple[str, Dict[str, Any]]]: + self, sam_template: dict[str, Any], macro_resolver: ResourceTypeResolver + ) -> list[tuple[str, dict[str, Any]]]: """ Returns a list of resources to iterate, order them based on the following order: @@ -264,7 +264,7 @@ def _get_resources_to_iterate( :param dict sam_template: SAM template :param macro_resolver: Resolver that knows if a resource can be processed or not - :return list: List containing tuple of (logicalId, resource_dict) in the order of processing + :return list: list containing tuple of (logicalId, resource_dict) in the order of processing """ functions = [] @@ -294,26 +294,26 @@ def _get_resources_to_iterate( return functions + statemachines + apis + others + connectors @staticmethod - def _update_resources(connectors_list: List[Resource]) -> Dict[str, Any]: + def _update_resources(connectors_list: list[Resource]) -> dict[str, Any]: connector_resources = {} for connector in connectors_list: connector_resources.update(connector.to_dict()) return connector_resources @staticmethod - def _delete_connectors_attribute(resources: Dict[str, Any]) -> None: + def _delete_connectors_attribute(resources: dict[str, Any]) -> None: for resource in resources.values(): if "Connectors" not in resource: continue del resource["Connectors"] - def _get_embedded_connectors(self, resources: Dict[str, Any]) -> List[Resource]: + def _get_embedded_connectors(self, resources: dict[str, Any]) -> list[Resource]: """ Loops through the SAM Template resources to find any connectors that have been attached to the resources. Converts those attached connectors into Connector resources and returns a list of them - :param dict resources: Dict of resources from the SAM template - :return List[SamConnector]: List of the generated SAM Connectors + :param dict resources: dict of resources from the SAM template + :return list[SamConnector]: list of the generated SAM Connectors """ connectors = [] @@ -363,7 +363,7 @@ def _get_generated_connector( source_logical_id: str, full_connector_logical_id: str, connector_logical_id: str, - connector_dict: Dict[str, Any], + connector_dict: dict[str, Any], ) -> Resource: """ Generates the connector resource from the embedded connector @@ -402,12 +402,12 @@ def _get_generated_connector( return SamConnector.from_dict(full_connector_logical_id, connector) -def prepare_plugins(plugins: Optional[List[BasePlugin]], parameters: Optional[Dict[str, Any]] = None) -> SamPlugins: +def prepare_plugins(plugins: list[BasePlugin] | None, parameters: dict[str, Any] | None = None) -> SamPlugins: """ Creates & returns a plugins object with the given list of plugins installed. In addition to the given plugins, we will also install a few "required" plugins that are necessary to provide complete support for SAM template spec. - :param plugins: list of samtranslator.plugins.BasePlugin plugins: List of plugins to install + :param plugins: list of samtranslator.plugins.BasePlugin plugins: list of plugins to install :param parameters: Dictionary of parameter values :return samtranslator.plugins.SamPlugins: Instance of `SamPlugins` """ diff --git a/samtranslator/translator/verify_logical_id.py b/samtranslator/translator/verify_logical_id.py index 473c9e5368..7c52634da3 100644 --- a/samtranslator/translator/verify_logical_id.py +++ b/samtranslator/translator/verify_logical_id.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from samtranslator.model import Resource @@ -23,16 +23,14 @@ } -def verify_unique_logical_id(resource: Resource, existing_resources: Dict[str, Any]) -> bool: +def verify_unique_logical_id(resource: Resource, existing_resources: dict[str, Any]) -> bool: """Return true if the logical id is unique.""" # new resource logicalid exists in the template before transform if resource.logical_id is None or resource.logical_id not in existing_resources: return True # new resource logicalid is in the do_not_resolve list - if ( + return bool( resource.resource_type in do_not_verify and existing_resources[resource.logical_id]["Type"] in do_not_verify[resource.resource_type] - ): - return True - return False + ) diff --git a/samtranslator/utils/actions.py b/samtranslator/utils/actions.py index 921914a047..42e1ad1057 100644 --- a/samtranslator/utils/actions.py +++ b/samtranslator/utils/actions.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Any, Dict +from typing import Any class Action(ABC): @@ -9,14 +9,14 @@ class Action(ABC): """ @abstractmethod - def execute(self, template: Dict[str, Any]) -> Dict[str, Any]: + def execute(self, template: dict[str, Any]) -> dict[str, Any]: pass class ResolveDependsOn(Action): DependsOn = "DependsOn" - def __init__(self, resolution_data: Dict[str, str]): + def __init__(self, resolution_data: dict[str, str]): """ Initializes ResolveDependsOn. Where data necessary to resolve execute can be provided. @@ -24,7 +24,7 @@ def __init__(self, resolution_data: Dict[str, str]): """ self.resolution_data = resolution_data - def execute(self, template: Dict[str, Any]) -> Dict[str, Any]: + def execute(self, template: dict[str, Any]) -> dict[str, Any]: """ Resolve DependsOn when logical ids get changed when transforming (ex: AWS::Serverless::LayerVersion) @@ -50,7 +50,7 @@ def execute(self, template: Dict[str, Any]) -> Dict[str, Any]: template[self.DependsOn] = changed_logical_id return template - def _can_handle_depends_on(self, input_dict: Dict[str, Any]) -> bool: + def _can_handle_depends_on(self, input_dict: dict[str, Any]) -> bool: """ Checks if the input dictionary is of length one and contains "DependsOn" diff --git a/samtranslator/utils/cfn_dynamic_references.py b/samtranslator/utils/cfn_dynamic_references.py index f5b340fddd..6c4eabf2f9 100644 --- a/samtranslator/utils/cfn_dynamic_references.py +++ b/samtranslator/utils/cfn_dynamic_references.py @@ -12,6 +12,4 @@ def is_dynamic_reference(_input: Any) -> bool: :return: True, if yes """ pattern = re.compile("^{{resolve:([a-z-]+):(.+)}}$") - if _input is not None and isinstance(_input, str) and pattern.match(_input): - return True - return False + return bool(_input is not None and isinstance(_input, str) and pattern.match(_input)) diff --git a/samtranslator/utils/py27hash_fix.py b/samtranslator/utils/py27hash_fix.py index 17ee3b257e..1222748b11 100644 --- a/samtranslator/utils/py27hash_fix.py +++ b/samtranslator/utils/py27hash_fix.py @@ -1,11 +1,11 @@ -""" -""" +""" """ import copy import ctypes import json import logging -from typing import Any, Dict, Iterator, List, Optional, cast +from collections.abc import Iterator +from typing import Any, cast from samtranslator.parser.parser import Parser from samtranslator.third_party.py27hash.hash import Hash @@ -21,7 +21,7 @@ def to_py27_compatible_template( # noqa: PLR0912 - template: Dict[str, Any], parameter_values: Optional[Dict[str, Any]] = None + template: dict[str, Any], parameter_values: dict[str, Any] | None = None ) -> None: """ Convert an input template to a py27hash-compatible template. This function has to be run before any @@ -96,8 +96,8 @@ def to_py27_compatible_template( # noqa: PLR0912 parameter_values[key] = _convert_to_py27_type(val) # type: ignore[no-untyped-call] -def undo_mark_unicode_str_in_template(template_dict: Dict[str, Any]) -> Dict[str, Any]: - return cast(Dict[str, Any], json.loads(json.dumps(template_dict))) +def undo_mark_unicode_str_in_template(template_dict: dict[str, Any]) -> dict[str, Any]: + return cast(dict[str, Any], json.loads(json.dumps(template_dict))) class Py27UniStr(unicode_string_type): @@ -130,7 +130,7 @@ def __deepcopy__(self, memo): # type: ignore[no-untyped-def] return self # strings are immutable def _get_py27_hash(self) -> int: - h: Optional[int] = getattr(self, "_py27_hash", None) + h: int | None = getattr(self, "_py27_hash", None) if h is None: self._py27_hash = h = ctypes.c_size_t(Hash.hash(self)).value return h @@ -170,7 +170,7 @@ class Py27Keys: def __init__(self) -> None: super().__init__() self.debug = False - self.keyorder: Dict[int, str] = {} + self.keyorder: dict[int, str] = {} self.size = 0 # current size of the keys, equivalent to ma_used in dictobject.c self.fill = 0 # increment count when a key is added, equivalent to ma_fill in dictobject.c self.mask = MINSIZE - 1 # Python2 default dict size @@ -262,7 +262,7 @@ def add(self, key): # type: ignore[no-untyped-def] # Python2 dict increases size by a factor of 4 for small dict, and 2 for large dict self._resize(self.size * (2 if self.size > self._LARGE_DICT_SIZE_THRESHOLD else 4)) # type: ignore[no-untyped-call] - def keys(self) -> List[str]: + def keys(self) -> list[str]: """Return keys in Python2 order""" return [self.keyorder[key] for key in sorted(self.keyorder.keys()) if self.keyorder[key] is not self.DUMMY] @@ -520,7 +520,7 @@ def __str__(self) -> str: def __repr__(self) -> str: """ - Create a string version of this Dict + Create a string version of this dict Returns ------- diff --git a/samtranslator/utils/traverse.py b/samtranslator/utils/traverse.py index ebd34584a0..257d89b02e 100644 --- a/samtranslator/utils/traverse.py +++ b/samtranslator/utils/traverse.py @@ -1,11 +1,11 @@ -from typing import Any, Dict, List +from typing import Any from samtranslator.utils.actions import Action def traverse( input_value: Any, - actions: List[Action], + actions: list[Action], ) -> Any: """ Driver method that performs the actual traversal of input and calls the execute method of the provided actions. @@ -13,7 +13,7 @@ def traverse( Traversal Algorithm: Imagine the input dictionary/list as a tree. We are doing a Pre-Order tree traversal here where we first - process the root node before going to its children. Dict and Lists are the only two iterable nodes. + process the root node before going to its children. dict and Lists are the only two iterable nodes. Everything else is a leaf node. :param input_value: Any primitive type (dict, array, string etc) whose value might contain a changed value @@ -34,8 +34,8 @@ def traverse( def _traverse_dict( - input_dict: Dict[str, Any], - actions: List[Action], + input_dict: dict[str, Any], + actions: list[Action], ) -> Any: """ Traverse a dictionary to resolves changed values on every value @@ -51,13 +51,13 @@ def _traverse_dict( def _traverse_list( - input_list: List[Any], - actions: List[Action], + input_list: list[Any], + actions: list[Action], ) -> Any: """ Traverse a list to resolve changed values on every element - :param input_list: List of input + :param input_list: list of input :param actions: This is just to pass it to the template partition :return: Modified list with values functions resolved """ diff --git a/samtranslator/utils/types.py b/samtranslator/utils/types.py index 88d9db614a..a9e43f4597 100644 --- a/samtranslator/utils/types.py +++ b/samtranslator/utils/types.py @@ -1,7 +1,7 @@ """Type related utils.""" -from typing import Any, Dict, TypeVar, Union +from typing import Any, TypeVar, Union T = TypeVar("T") -Intrinsicable = Union[Dict[str, Any], T] +Intrinsicable = Union[dict[str, Any], T] diff --git a/samtranslator/utils/utils.py b/samtranslator/utils/utils.py index ab952efdfc..b54a7f83ee 100644 --- a/samtranslator/utils/utils.py +++ b/samtranslator/utils/utils.py @@ -1,13 +1,13 @@ import copy -from typing import Any, List, Optional, Union, cast +from typing import Any, Union, cast -def as_array(x: Any) -> List[Any]: +def as_array(x: Any) -> list[Any]: """Convert value to list if it already isn't.""" return x if isinstance(x, list) else [x] -def insert_unique(xs: Any, vs: Any) -> List[Any]: +def insert_unique(xs: Any, vs: Any) -> list[Any]: """ Return copy of `xs` extended with values of `vs` that do not exist in `xs`. @@ -20,7 +20,7 @@ def insert_unique(xs: Any, vs: Any) -> List[Any]: if v not in xs: xs.append(v) - return cast(List[Any], xs) # mypy doesn't recognize it + return cast(list[Any], xs) # mypy doesn't recognize it class InvalidValueType(Exception): @@ -31,7 +31,7 @@ def __init__(self, relative_path: str) -> None: super().__init__("It should be a map") -def dict_deep_get(d: Any, path: Union[str, List[str]]) -> Optional[Any]: +def dict_deep_get(d: Any, path: Union[str, list[str]]) -> Any | None: """ Get the value deep in the dict. diff --git a/samtranslator/validator/value_validator.py b/samtranslator/validator/value_validator.py index 699c86527b..26ee222112 100644 --- a/samtranslator/validator/value_validator.py +++ b/samtranslator/validator/value_validator.py @@ -1,6 +1,6 @@ """A plug-able validator to help raise exception when some value is unexpected.""" -from typing import Any, Dict, Generic, Optional, TypeVar, cast +from typing import Any, Generic, TypeVar, cast from samtranslator.model.exceptions import ( ExpectedType, @@ -14,7 +14,7 @@ class _ResourcePropertyValueValidator(Generic[T]): - value: Optional[T] + value: T | None resource_id: str key_path: str is_sam_event: bool @@ -22,7 +22,7 @@ class _ResourcePropertyValueValidator(Generic[T]): def __init__( self, - value: Optional[T], + value: T | None, resource_id: str, key_path: str, is_sam_event: bool = False, @@ -35,14 +35,14 @@ def __init__( self.is_resource_attribute = is_resource_attribute @property - def resource_logical_id(self) -> Optional[str]: + def resource_logical_id(self) -> str | None: return None if self.is_sam_event else self.resource_id @property - def event_id(self) -> Optional[str]: + def event_id(self) -> str | None: return self.resource_id if self.is_sam_event else None - def to_be_a(self, expected_type: ExpectedType, message: Optional[str] = "") -> T: + def to_be_a(self, expected_type: ExpectedType, message: str | None = "") -> T: """ Validate the type of the value and return the value if valid. @@ -66,7 +66,7 @@ def to_be_a(self, expected_type: ExpectedType, message: Optional[str] = "") -> T # mypy is not smart to derive class from expected_type.value[1], ignore types: return self.value # type: ignore - def to_not_be_none(self, message: Optional[str] = "") -> T: + def to_not_be_none(self, message: str | None = "") -> T: """ Validate the value is not None and return the value if valid. @@ -85,17 +85,17 @@ def to_not_be_none(self, message: Optional[str] = "") -> T: # # alias methods: # - def to_be_a_map(self, message: Optional[str] = "") -> Dict[str, Any]: + def to_be_a_map(self, message: str | None = "") -> dict[str, Any]: """ - Return the value with type hint "Dict[str, Any]". + Return the value with type hint "dict[str, Any]". Raise InvalidResourceException/InvalidEventException if the value is not. """ - return cast(Dict[str, Any], self.to_be_a(ExpectedType.MAP, message)) + return cast(dict[str, Any], self.to_be_a(ExpectedType.MAP, message)) - def to_be_a_list(self, message: Optional[str] = "") -> T: + def to_be_a_list(self, message: str | None = "") -> T: return self.to_be_a(ExpectedType.LIST, message) - def to_be_a_list_of(self, expected_type: ExpectedType, message: Optional[str] = "") -> T: + def to_be_a_list_of(self, expected_type: ExpectedType, message: str | None = "") -> T: """ Return the value with type hint "List[T]". Raise InvalidResourceException/InvalidEventException if the value is not. @@ -107,21 +107,21 @@ def to_be_a_list_of(self, expected_type: ExpectedType, message: Optional[str] = ) return value - def to_be_a_string(self, message: Optional[str] = "") -> str: + def to_be_a_string(self, message: str | None = "") -> str: """ Return the value with type hint "str". Raise InvalidResourceException/InvalidEventException if the value is not. """ return cast(str, self.to_be_a(ExpectedType.STRING, message)) - def to_be_an_integer(self, message: Optional[str] = "") -> int: + def to_be_an_integer(self, message: str | None = "") -> int: """ Return the value with type hint "int". Raise InvalidResourceException/InvalidEventException if the value is not. """ return cast(int, self.to_be_a(ExpectedType.INTEGER, message)) - def to_be_a_bool(self, message: Optional[str] = "") -> bool: + def to_be_a_bool(self, message: str | None = "") -> bool: """ Return the value with type hint "bool". Raise InvalidResourceException/InvalidEventException if the value is not. diff --git a/schema_source/cfn_schema_generator.py b/schema_source/cfn_schema_generator.py index 8c2c5ffb5a..91d900c0a4 100644 --- a/schema_source/cfn_schema_generator.py +++ b/schema_source/cfn_schema_generator.py @@ -7,7 +7,7 @@ import gzip import json from pathlib import Path -from typing import Any, Dict, cast +from typing import Any, cast import requests @@ -231,7 +231,7 @@ def generate(self, output_file: str = ".tmp/cloudformation.schema.json") -> None json_str = json_str.replace("<", "\\u003c").replace(">", "\\u003e") f.write(json_str) - def _download_spec(self) -> Dict[str, Any]: + def _download_spec(self) -> dict[str, Any]: """Download and parse CloudFormation specification""" response = requests.get(self.spec_url, timeout=30) response.raise_for_status() @@ -241,10 +241,10 @@ def _download_spec(self) -> Dict[str, Any]: if content.startswith(b"\x1f\x8b"): # gzip magic number content = gzip.decompress(content) - result: Dict[str, Any] = json.loads(content) + result: dict[str, Any] = json.loads(content) return result - def _generate_schema(self, spec: Dict[str, Any]) -> Dict[str, Any]: + def _generate_schema(self, spec: dict[str, Any]) -> dict[str, Any]: """Generate JSON schema from CloudFormation specification""" resources = spec.get("ResourceTypes", {}) properties = spec.get("PropertyTypes", {}) @@ -254,8 +254,8 @@ def _generate_schema(self, spec: Dict[str, Any]) -> Dict[str, Any]: resource_refs.append({"$ref": "#/definitions/CustomResource"}) # Start with main schema template and fill in the details - main_properties = cast(Dict[str, Any], MAIN_SCHEMA_TEMPLATE["properties"]) - resources_property = cast(Dict[str, Any], main_properties["Resources"]) + main_properties = cast(dict[str, Any], MAIN_SCHEMA_TEMPLATE["properties"]) + resources_property = cast(dict[str, Any], main_properties["Resources"]) schema = { **MAIN_SCHEMA_TEMPLATE, @@ -269,7 +269,7 @@ def _generate_schema(self, spec: Dict[str, Any]) -> Dict[str, Any]: } # Build definitions from templates - definitions: Dict[str, Any] = {} + definitions: dict[str, Any] = {} definitions["Parameter"] = PARAMETER_SCHEMA_TEMPLATE definitions["CustomResource"] = CUSTOM_RESOURCE_SCHEMA_TEMPLATE @@ -285,8 +285,8 @@ def _generate_schema(self, spec: Dict[str, Any]) -> Dict[str, Any]: return schema def _generate_resource_schema( - self, name: str, resource: Dict[str, Any], is_custom_property: bool - ) -> Dict[str, Any]: + self, name: str, resource: dict[str, Any], is_custom_property: bool + ) -> dict[str, Any]: """Generate schema for a CloudFormation resource""" properties = resource.get("Properties", {}) required = sorted([prop_name for prop_name, prop in properties.items() if prop.get("Required", False)]) @@ -327,21 +327,21 @@ def _generate_resource_schema( # Add optional policies for specific resources if name in RESOURCES_WITH_CREATION_POLICY: - properties_obj = cast(Dict[str, Any], resource_schema["properties"]) + properties_obj = cast(dict[str, Any], resource_schema["properties"]) properties_obj["CreationPolicy"] = {"type": "object"} if name in RESOURCES_WITH_UPDATE_POLICY: - properties_obj = cast(Dict[str, Any], resource_schema["properties"]) + properties_obj = cast(dict[str, Any], resource_schema["properties"]) properties_obj["UpdatePolicy"] = {"type": "object"} return resource_schema def _generate_property_schema( # noqa: PLR0911 - self, name: str, prop: Dict[str, Any], parent: str - ) -> Dict[str, Any]: + self, name: str, prop: dict[str, Any], parent: str + ) -> dict[str, Any]: """Generate schema for a CloudFormation property""" # Extract resource name from parent (e.g., "AWS::S3::Bucket" from "AWS::S3::Bucket.Property") - resource_name = parent.split(".")[0] if "." in parent else parent + resource_name = parent.split(".", maxsplit=1)[0] if "." in parent else parent # Handle polymorphic properties (simplified) if self._is_polymorphic(prop): @@ -399,7 +399,7 @@ def _generate_property_schema( # noqa: PLR0911 return {"type": "object"} - def _is_polymorphic(self, prop: Dict[str, Any]) -> bool: + def _is_polymorphic(self, prop: dict[str, Any]) -> bool: """Check if property can be multiple different types""" return bool( prop.get("PrimitiveTypes") diff --git a/schema_source/sam.schema.json b/schema_source/sam.schema.json index 5cbca5201a..a6082c1d01 100644 --- a/schema_source/sam.schema.json +++ b/schema_source/sam.schema.json @@ -1858,10 +1858,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, @@ -1880,10 +1880,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, @@ -1902,10 +1902,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, @@ -3804,10 +3804,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, @@ -4130,10 +4130,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, @@ -4152,10 +4152,10 @@ "items": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ] }, diff --git a/setup.py b/setup.py index 69a68d14da..f11905fcee 100755 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ """ AWS SAM Serverless Application Model """ + import io import os import re @@ -78,7 +79,7 @@ def read_requirements(req="base.txt"): "NOTICE", "THIRD_PARTY_LICENSES", ), - python_requires=">=3.8, <=4.0, !=4.0", + python_requires=">=3.10, <=4.0, !=4.0", install_requires=read_requirements("base.txt"), include_package_data=True, extras_require={"dev": read_requirements("dev.txt")}, @@ -93,10 +94,11 @@ def read_requirements(req="base.txt"): "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Internet", "Topic :: Software Development :: Build Tools", "Topic :: Utilities", diff --git a/tests/model/capacity_provider/test_generators.py b/tests/model/capacity_provider/test_generators.py index e7c2f6f497..dfc785c618 100644 --- a/tests/model/capacity_provider/test_generators.py +++ b/tests/model/capacity_provider/test_generators.py @@ -1,4 +1,3 @@ -from typing import List from unittest import TestCase from unittest.mock import patch @@ -220,5 +219,5 @@ def test_create_operator_role(self): # Verify passthrough attributes self.assertEqual(role_dict[logical_id]["Condition"], "MyCondition") - def extract_resource(self, resource_array: List[Resource]): + def extract_resource(self, resource_array: list[Resource]): return {r.logical_id: r.to_dict()[r.logical_id] for r in resource_array} diff --git a/tests/ruff.toml b/tests/ruff.toml index 79e96b2a19..6f09c17f01 100644 --- a/tests/ruff.toml +++ b/tests/ruff.toml @@ -1,8 +1,8 @@ # black formatter takes care of the line length -line-length = 999 +line-length = 320 -# Mininal python version we support is 3.8 -target-version = "py38" +# Minimal python version we support is 3.10 +target-version = "py310" # The code quality of tests can be a bit lower compared to samtranslator lint.select = [ diff --git a/tests/schema/test_cfn_schema_generator.py b/tests/schema/test_cfn_schema_generator.py index def397b022..b91774e057 100644 --- a/tests/schema/test_cfn_schema_generator.py +++ b/tests/schema/test_cfn_schema_generator.py @@ -79,7 +79,7 @@ def setUp(self): self.output_folder = "tests/schema/cfn_schema_generator/output_schema" @parameterized.expand( - lambda: _get_test_cases_for_schema_generation(), + _get_test_cases_for_schema_generation, skip_on_empty=True, ) def test_schema_generation(self, case_name): diff --git a/tests/test_import.py b/tests/test_import.py index dcdda124ff..4a5fb710ac 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -3,7 +3,6 @@ import subprocess import sys from pathlib import Path -from typing import List from unittest import TestCase from parameterized import parameterized @@ -11,8 +10,8 @@ _PROJECT_ROOT = Path(__file__).parent.parent -def scan_modules_recursively(module_name: str = "samtranslator") -> List[str]: - all_modules: List[str] = [module_name] +def scan_modules_recursively(module_name: str = "samtranslator") -> list[str]: + all_modules: list[str] = [module_name] for submodule in pkgutil.iter_modules([os.path.join(_PROJECT_ROOT, module_name.replace(".", os.path.sep))]): submodule_name = module_name + "." + submodule.name all_modules += scan_modules_recursively(submodule_name) diff --git a/tests/test_model.py b/tests/test_model.py index 9b78fed9f0..c88f728b5b 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import Any from unittest import TestCase from unittest.mock import Mock @@ -221,7 +221,7 @@ class NewSamResource(SamResourceMacro): property_types = {} referable_properties = {"prop1": "resource_type1", "prop2": "resource_type2", "prop3": "resource_type3"} - def to_cloudformation(self, **kwargs: Any) -> List[Any]: + def to_cloudformation(self, **kwargs: Any) -> list[Any]: return [] sam_resource = NewSamResource("SamLogicalId") @@ -245,7 +245,7 @@ class NewSamResource(SamResourceMacro): property_types = {} referable_properties = {"prop1": "resource_type1", "prop2": "resource_type2", "prop3": "resource_type3"} - def to_cloudformation(self, **kwargs: Any) -> List[Any]: + def to_cloudformation(self, **kwargs: Any) -> list[Any]: return [] sam_resource1 = NewSamResource("SamLogicalId1") @@ -274,7 +274,7 @@ class NewSamResource(SamResourceMacro): property_types = {} referable_properties = {"prop1": "foo", "prop2": "bar"} - def to_cloudformation(self, **kwargs: Any) -> List[Any]: + def to_cloudformation(self, **kwargs: Any) -> list[Any]: return [] sam_resource = NewSamResource("SamLogicalId") @@ -292,7 +292,7 @@ class NewSamResource(SamResourceMacro): property_types = {} referable_properties = {} - def to_cloudformation(self, **kwargs: Any) -> List[Any]: + def to_cloudformation(self, **kwargs: Any) -> list[Any]: return [] sam_resource = NewSamResource("SamLogicalId") @@ -309,7 +309,7 @@ class NewSamResource(SamResourceMacro): property_types = {} referable_properties = {"prop1": "resource_type1"} - def to_cloudformation(self, **kwargs: Any) -> List[Any]: + def to_cloudformation(self, **kwargs: Any) -> list[Any]: return [] sam_resource = NewSamResource("SamLogicalId") @@ -326,7 +326,7 @@ class NewSamResource(SamResourceMacro): property_types = {} referable_properties = {"prop1": "resource_type1"} - def to_cloudformation(self, **kwargs: Any) -> List[Any]: + def to_cloudformation(self, **kwargs: Any) -> list[Any]: return [] sam_resource = NewSamResource("SamLogicalId") @@ -420,23 +420,23 @@ def setUp(self): # Test setting properties using BaseModel from samtranslator.internal.schema_source.common class TestSettingProperties(BaseModel): - NestedVar1: Optional[str] = None - NestedVar2: Optional[str] = None + NestedVar1: str | None = None + NestedVar2: str | None = None # Comprehensive schema class for testing using BaseModel from samtranslator.internal.schema_source.common class TestProperties(BaseModel): - ConditionalVar1: Optional[int] = None - ConditionalVar2: Optional[int] = None - ConditionalVar3: Optional[int] = None - ExclusiveVar1: Optional[str] = None - ExclusiveVar2: Optional[str] = None - ExclusiveVar3: Optional[str] = None - InclusiveVar1: Optional[bool] = None - InclusiveVar2: Optional[bool] = None - InclusiveVar3: Optional[bool] = None - NestedSetting1: Optional[TestSettingProperties] = None - NestedSetting2: Optional[TestSettingProperties] = None - NestedSetting3: Optional[TestSettingProperties] = None + ConditionalVar1: int | None = None + ConditionalVar2: int | None = None + ConditionalVar3: int | None = None + ExclusiveVar1: str | None = None + ExclusiveVar2: str | None = None + ExclusiveVar3: str | None = None + InclusiveVar1: bool | None = None + InclusiveVar2: bool | None = None + InclusiveVar3: bool | None = None + NestedSetting1: TestSettingProperties | None = None + NestedSetting2: TestSettingProperties | None = None + NestedSetting3: TestSettingProperties | None = None self.TestProperties = TestProperties self.TestSettingProperties = TestSettingProperties diff --git a/tests/translator/output/error_function_invalid_event_type.json b/tests/translator/output/error_function_invalid_event_type.json index 99038c8f52..3b83356297 100644 --- a/tests/translator/output/error_function_invalid_event_type.json +++ b/tests/translator/output/error_function_invalid_event_type.json @@ -4,20 +4,20 @@ "Number of errors found: 3. ", "Resource with id [FunctionApiTypeError] is invalid. ", "Event with id [ApiEvent] is invalid. ", - "Resource dict has missing or invalid value for key Type. ", - "Event Type is: API. ", + "Resource dict has missing or invalid value for key type. ", + "Event type is: API. ", "Resource with id [FunctionNoEventType] is invalid. ", "Event with id [MissingType] is invalid. ", - "Resource dict has missing or invalid value for key Type. ", - "Event Type is: None. ", + "Resource dict has missing or invalid value for key type. ", + "Event type is: None. ", "Resource with id [TestFunction] is invalid. ", "Event with id [FileUploaded] is invalid. ", "'NoneType' object has no attribute 'get'" ], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 3. Resource with id [FunctionApiTypeError] is invalid. Event with id [ApiEvent] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: API. Resource with id [FunctionNoEventType] is invalid. Event with id [MissingType] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: None. Resource with id [TestFunction] is invalid. Event with id [FileUploaded] is invalid. 'NoneType' object has no attribute 'get'", + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 3. Resource with id [FunctionApiTypeError] is invalid. Event with id [ApiEvent] is invalid. Resource dict has missing or invalid value for key type. Event type is: API. Resource with id [FunctionNoEventType] is invalid. Event with id [MissingType] is invalid. Resource dict has missing or invalid value for key type. Event type is: None. Resource with id [TestFunction] is invalid. Event with id [FileUploaded] is invalid. 'NoneType' object has no attribute 'get'", "errors": [ { - "errorMessage": "Resource with id [FunctionApiTypeError] is invalid. Event with id [ApiEvent] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: API. Resource with id [FunctionNoEventType] is invalid. Event with id [MissingType] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: None." + "errorMessage": "Resource with id [FunctionApiTypeError] is invalid. Event with id [ApiEvent] is invalid. Resource dict has missing or invalid value for key type. Event type is: API. Resource with id [FunctionNoEventType] is invalid. Event with id [MissingType] is invalid. Resource dict has missing or invalid value for key type. Event type is: None." } ] } diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 89840cae9f..c1c64e985b 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -1169,7 +1169,7 @@ def get_policy_mock(): def get_deployment_key(fragment): - logical_id, value = get_resource_by_type(fragment, "AWS::ApiGateway::Deployment") + logical_id, _value = get_resource_by_type(fragment, "AWS::ApiGateway::Deployment") return logical_id