Skip to content

Commit 8bfcb92

Browse files
authored
Merge pull request #3135 from aws/release-v1.66.0
Release 1.66.0 (to main)
2 parents 5252262 + a832112 commit 8bfcb92

32 files changed

+32774
-5804
lines changed

bin/_file_formatter.py

+19-21
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import sys
55
from abc import ABC, abstractmethod
6+
from pathlib import Path
67
from typing import Type
78

89

@@ -44,31 +45,28 @@ def file_extension() -> str:
4445
def config_additional_args(cls) -> None: # noqa: empty-method-without-abstract-decorator
4546
"""Optionally configure additional args to arg parser."""
4647

47-
def process_file(self, file_path: str) -> None:
48-
with open(file_path, encoding="utf-8") as f:
49-
file_str = f.read()
50-
try:
51-
formatted_file_str = self.format_str(file_str)
52-
except self.decode_exception() as error:
53-
raise ValueError(f"{file_path}: Cannot decode the file content") from error
54-
except Exception as error:
55-
raise ValueError(f"{file_path}: Fail to process") from error
48+
def process_file(self, file_path: Path) -> None:
49+
file_str = file_path.read_text(encoding="utf-8")
50+
try:
51+
formatted_file_str = self.format_str(file_str)
52+
except self.decode_exception() as error:
53+
raise ValueError(f"{file_path}: Cannot decode the file content") from error
54+
except Exception as error:
55+
raise ValueError(f"{file_path}: Fail to process") from error
5656
if file_str != formatted_file_str:
5757
if self.args.write:
58-
with open(file_path, "w", encoding="utf-8") as f:
59-
f.write(formatted_file_str)
58+
Path(file_path).write_text(formatted_file_str, encoding="utf-8")
6059
print(f"reformatted {file_path}")
6160
if self.args.check:
6261
print(f"would reformat {file_path}")
6362
self.unformatted_file_count += 1
6463
self.scanned_file_found += 1
6564

66-
def process_directory(self, directory_path: str) -> None:
65+
def process_directory(self, directory_path: Path) -> None:
6766
for root, _dirs, files in os.walk(directory_path):
6867
for file in files:
69-
file_path = os.path.join(root, file)
70-
_, extension = os.path.splitext(file_path)
71-
if extension != self.file_extension():
68+
file_path = Path(root) / file
69+
if file_path.suffix != self.file_extension():
7270
continue
7371
self.process_file(file_path)
7472

@@ -112,15 +110,15 @@ def main(cls) -> None:
112110
args = cls.arg_parser.parse_args()
113111
formatter = cls(args)
114112

115-
for path in args.paths:
116-
if not os.path.exists(path):
113+
for _path in args.paths:
114+
path = Path(_path)
115+
if not path.exists():
117116
raise ValueError(f"{path}: No such file or directory")
118-
if os.path.isfile(path):
119-
_, extension = os.path.splitext(path)
120-
if extension != cls.file_extension():
117+
if path.is_file():
118+
if path.suffix != cls.file_extension():
121119
raise ValueError(f"{path}: Not a format-able file")
122120
formatter.process_file(path)
123-
elif os.path.isdir(path):
121+
elif path.is_dir():
124122
formatter.process_directory(path)
125123
else:
126124
raise ValueError(f"{path}: Unsupported path")

bin/add_transform_test.py

+23-25
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"""Automatically create transform tests input and output files given an input template."""
33
import argparse
44
import json
5-
import os
65
import shutil
76
import subprocess
87
import sys
@@ -17,8 +16,8 @@
1716
from samtranslator.translator.transform import transform
1817
from samtranslator.yaml_helper import yaml_parse
1918

20-
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
21-
TRANSFORM_TEST_DIR = os.path.join(SCRIPT_DIR, "..", "tests", "translator")
19+
SCRIPT_DIR = Path(__file__).parent
20+
TRANSFORM_TEST_DIR = SCRIPT_DIR.parent / "tests" / "translator"
2221

2322
iam_client = boto3.client("iam")
2423

@@ -42,13 +41,13 @@
4241
CLI_OPTIONS = parser.parse_args()
4342

4443

45-
def read_json_file(file_path: str) -> Dict[str, Any]:
46-
template: Dict[str, Any] = json.loads(Path(file_path).read_text(encoding="utf-8"))
44+
def read_json_file(file_path: Path) -> Dict[str, Any]:
45+
template: Dict[str, Any] = json.loads(file_path.read_text(encoding="utf-8"))
4746
return template
4847

4948

50-
def write_json_file(obj: Dict[str, Any], file_path: str) -> None:
51-
with open(file_path, "w", encoding="utf-8") as f:
49+
def write_json_file(obj: Dict[str, Any], file_path: Path) -> None:
50+
with file_path.open("w", encoding="utf-8") as f:
5251
json.dump(obj, f, indent=2, sort_keys=True)
5352

5453

@@ -64,24 +63,23 @@ def add_regional_endpoint_configuration_if_needed(template: Dict[str, Any]) -> D
6463
return template
6564

6665

67-
def replace_aws_partition(partition: str, file_path: str) -> None:
66+
def replace_aws_partition(partition: str, file_path: Path) -> None:
6867
template = read_json_file(file_path)
69-
with open(file_path, "w") as file:
70-
updated_template = json.loads(json.dumps(template).replace("arn:aws:", f"arn:{partition}:"))
71-
file.write(json.dumps(updated_template, indent=2))
68+
updated_template = json.loads(json.dumps(template).replace("arn:aws:", f"arn:{partition}:"))
69+
file_path.write_text(json.dumps(updated_template, indent=2), encoding="utf-8")
7270
print(f"Transform Test output files generated {file_path}")
7371

7472

75-
def generate_transform_test_output_files(input_file_path: str, file_basename: str) -> None:
73+
def generate_transform_test_output_files(input_file_path: Path, file_basename: str) -> None:
7674
output_file_option = file_basename + ".json"
7775

78-
with open(os.path.join(input_file_path)) as f:
76+
with input_file_path.open(encoding="utf-8") as f:
7977
manifest = yaml_parse(f) # type: ignore[no-untyped-call]
8078

8179
transform_test_output_paths = {
82-
"aws": ("us-west-2", os.path.join(TRANSFORM_TEST_DIR, "output", output_file_option)),
83-
"aws-cn": ("cn-north-1 ", os.path.join(TRANSFORM_TEST_DIR, "output/aws-cn/", output_file_option)),
84-
"aws-us-gov": ("us-gov-west-1", os.path.join(TRANSFORM_TEST_DIR, "output/aws-us-gov/", output_file_option)),
80+
"aws": ("us-west-2", TRANSFORM_TEST_DIR / "output" / output_file_option),
81+
"aws-cn": ("cn-north-1 ", TRANSFORM_TEST_DIR / "output" / "aws-cn" / output_file_option),
82+
"aws-us-gov": ("us-gov-west-1", TRANSFORM_TEST_DIR / "output" / "aws-us-gov" / output_file_option),
8583
}
8684

8785
for partition, (region, output_path) in transform_test_output_paths.items():
@@ -100,18 +98,18 @@ def generate_transform_test_output_files(input_file_path: str, file_basename: st
10098
replace_aws_partition(partition, output_path)
10199

102100

103-
def get_input_file_path() -> str:
101+
def get_input_file_path() -> Path:
104102
input_file_option = str(CLI_OPTIONS.template_file)
105-
return os.path.join(os.getcwd(), input_file_option)
103+
return Path.cwd() / input_file_option
106104

107105

108-
def copy_input_file_to_transform_test_dir(input_file_path: str, transform_test_input_path: str) -> None:
106+
def copy_input_file_to_transform_test_dir(input_file_path: Path, transform_test_input_path: Path) -> None:
109107
shutil.copyfile(input_file_path, transform_test_input_path)
110108
print(f"Transform Test input file generated {transform_test_input_path}")
111109

112110

113-
def verify_input_template(input_file_path: str): # type: ignore[no-untyped-def]
114-
if "arn:aws:" in Path(input_file_path).read_text(encoding="utf-8"):
111+
def verify_input_template(input_file_path: Path) -> None:
112+
if "arn:aws:" in input_file_path.read_text(encoding="utf-8"):
115113
print(
116114
"WARNING: hardcoded partition name detected. Consider replace it with pseudo parameter {AWS::Partition}",
117115
file=sys.stderr,
@@ -120,23 +118,23 @@ def verify_input_template(input_file_path: str): # type: ignore[no-untyped-def]
120118

121119
def format_test_files() -> None:
122120
subprocess.run(
123-
[sys.executable, os.path.join(SCRIPT_DIR, "json-format.py"), "--write", "tests"],
121+
[sys.executable, SCRIPT_DIR / "json-format.py", "--write", "tests"],
124122
check=True,
125123
)
126124

127125
subprocess.run(
128-
[sys.executable, os.path.join(SCRIPT_DIR, "yaml-format.py"), "--write", "tests"],
126+
[sys.executable, SCRIPT_DIR / "yaml-format.py", "--write", "tests"],
129127
check=True,
130128
)
131129

132130

133131
def main() -> None:
134132
input_file_path = get_input_file_path()
135-
file_basename = Path(input_file_path).stem
133+
file_basename = input_file_path.stem
136134

137135
verify_input_template(input_file_path)
138136

139-
transform_test_input_path = os.path.join(TRANSFORM_TEST_DIR, "input", file_basename + ".yaml")
137+
transform_test_input_path = TRANSFORM_TEST_DIR / "input" / (file_basename + ".yaml")
140138
copy_input_file_to_transform_test_dir(input_file_path, transform_test_input_path)
141139

142140
generate_transform_test_output_files(transform_test_input_path, file_basename)

bin/json-format.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#!/usr/bin/env python
22
"""JSON file formatter (without prettier)."""
3-
import os
43
import sys
4+
from pathlib import Path
55

6-
my_path = os.path.dirname(os.path.abspath(__file__))
7-
sys.path.insert(0, my_path + "/..")
6+
# To allow this script to be executed from other directories
7+
sys.path.insert(0, str(Path(__file__).absolute().parent.parent))
88

99
import json
1010
from typing import Type

bin/public_interface.py

+19-12
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@
88
This CLI tool helps automate the detection of compatibility-breaking changes.
99
"""
1010
import argparse
11+
import ast
1112
import importlib
1213
import inspect
1314
import json
1415
import os.path
1516
import pkgutil
17+
import string
1618
import sys
1719
from pathlib import Path
1820
from typing import Any, Dict, List, NamedTuple, Optional, Set, Union
1921

2022
_ARGUMENT_SELF = {"kind": "POSITIONAL_OR_KEYWORD", "name": "self"}
23+
_PRINTABLE_CHARS = set(string.printable)
2124

2225

2326
class InterfaceScanner:
@@ -51,20 +54,24 @@ def _scan_functions_in_module(self, module_name: str) -> None:
5154

5255
def _scan_variables_in_module(self, module_name: str) -> None:
5356
"""
54-
There is no method to verify if a module attribute is a constant,
55-
After some experiment, here we assume if an attribute is a value
56-
(without `__module__`) and not a module itself is a constant.
57-
57+
Use ast to find all assignments at the module level to find constants.
5858
Note: Class (and other types) should be treated as a variable too
5959
"""
60-
for constant_name, _ in inspect.getmembers(
61-
importlib.import_module(module_name),
62-
lambda obj: not hasattr(obj, "__module__") and not inspect.ismodule(obj),
63-
):
64-
if constant_name.startswith("_"):
65-
continue
66-
full_path = f"{module_name}.{constant_name}"
67-
self.variables.add(full_path)
60+
module_path = Path(module_name.replace(".", os.path.sep))
61+
if module_path.is_dir():
62+
module_path /= "__init__.py"
63+
else:
64+
module_path = module_path.with_suffix(".py")
65+
tree = ast.parse("".join([char for char in module_path.read_text() if char in _PRINTABLE_CHARS]))
66+
assignments: List[ast.Assign] = [node for node in ast.iter_child_nodes(tree) if isinstance(node, ast.Assign)]
67+
for assignment in assignments:
68+
for target in assignment.targets:
69+
if not isinstance(target, ast.Name):
70+
continue
71+
if target.id.startswith("_"):
72+
continue
73+
full_path = f"{module_name}.{target.id}"
74+
self.variables.add(full_path)
6875

6976
for class_name, _class in inspect.getmembers(importlib.import_module(module_name), inspect.isclass):
7077
# Skip imported and ones starting with "_"

bin/sam-translate.py

+20-24
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,17 @@
77
import argparse
88
import json
99
import logging
10-
import os
1110
import platform
1211
import subprocess
1312
import sys
1413
from functools import reduce
1514
from pathlib import Path
15+
from typing import List
1616

1717
import boto3
1818

1919
# To allow this script to be executed from other directories
20-
my_path = os.path.dirname(os.path.abspath(__file__))
21-
sys.path.insert(0, my_path + "/..")
20+
sys.path.insert(0, str(Path(__file__).absolute().parent.parent))
2221

2322
from samtranslator.model.exceptions import InvalidDocumentException
2423
from samtranslator.public.translator import ManagedPolicyLoader
@@ -67,7 +66,7 @@
6766
logging.basicConfig()
6867

6968

70-
def execute_command(command, args): # type: ignore[no-untyped-def]
69+
def execute_command(command: str, args: List[str]) -> None:
7170
try:
7271
aws_cmd = "aws" if platform.system().lower() != "windows" else "aws.cmd"
7372
command_with_args = [aws_cmd, "cloudformation", command, *list(args)]
@@ -83,9 +82,9 @@ def execute_command(command, args): # type: ignore[no-untyped-def]
8382
sys.exit(e.returncode)
8483

8584

86-
def package(input_file_path, output_file_path): # type: ignore[no-untyped-def]
85+
def package(input_file_path: Path) -> Path:
8786
template_file = input_file_path
88-
package_output_template_file = input_file_path + "._sam_packaged_.yaml"
87+
package_output_template_file = Path(str(input_file_path) + "._sam_packaged_.yaml")
8988
s3_bucket = cli_options.s3_bucket
9089
args = [
9190
"--template-file",
@@ -96,50 +95,47 @@ def package(input_file_path, output_file_path): # type: ignore[no-untyped-def]
9695
s3_bucket,
9796
]
9897

99-
execute_command("package", args) # type: ignore[no-untyped-call]
98+
execute_command("package", args)
10099

101100
return package_output_template_file
102101

103102

104-
def transform_template(input_file_path, output_file_path): # type: ignore[no-untyped-def]
105-
with open(input_file_path) as f:
103+
def transform_template(input_file_path: Path, output_file_path: Path): # type: ignore[no-untyped-def]
104+
with input_file_path.open() as f:
106105
sam_template = yaml_parse(f) # type: ignore[no-untyped-call]
107106

108107
try:
109108
cloud_formation_template = transform(sam_template, {}, ManagedPolicyLoader(iam_client))
110109
cloud_formation_template_prettified = json.dumps(cloud_formation_template, indent=1)
111110

112-
with open(output_file_path, "w") as f:
113-
f.write(cloud_formation_template_prettified)
111+
output_file_path.write_text(cloud_formation_template_prettified, encoding="utf-8")
114112

115-
print("Wrote transformed CloudFormation template to: " + output_file_path)
113+
print("Wrote transformed CloudFormation template to: ", output_file_path)
116114
except InvalidDocumentException as e:
117115
error_message = reduce(lambda message, error: message + " " + error.message, e.causes, e.message)
118116
LOG.error(error_message)
119117
errors = (cause.message for cause in e.causes)
120118
LOG.error(errors)
121119

122120

123-
def deploy(template_file): # type: ignore[no-untyped-def]
121+
def deploy(template_file: Path) -> None:
124122
capabilities = cli_options.capabilities
125123
stack_name = cli_options.stack_name
126124
args = ["--template-file", template_file, "--capabilities", capabilities, "--stack-name", stack_name]
127125

128-
execute_command("deploy", args) # type: ignore[no-untyped-call]
129-
130-
return package_output_template_file
126+
execute_command("deploy", args)
131127

132128

133129
if __name__ == "__main__":
134-
input_file_path = str(cli_options.template_file)
135-
output_file_path = str(cli_options.output_template)
130+
input_file_path = Path(cli_options.template_file)
131+
output_file_path = Path(cli_options.output_template)
136132

137133
if cli_options.command == "package":
138-
package_output_template_file = package(input_file_path, output_file_path) # type: ignore[no-untyped-call]
139-
transform_template(package_output_template_file, output_file_path) # type: ignore[no-untyped-call]
134+
package_output_template_file = package(input_file_path)
135+
transform_template(package_output_template_file, output_file_path)
140136
elif cli_options.command == "deploy":
141-
package_output_template_file = package(input_file_path, output_file_path) # type: ignore[no-untyped-call]
142-
transform_template(package_output_template_file, output_file_path) # type: ignore[no-untyped-call]
143-
deploy(output_file_path) # type: ignore[no-untyped-call]
137+
package_output_template_file = package(input_file_path)
138+
transform_template(package_output_template_file, output_file_path)
139+
deploy(output_file_path)
144140
else:
145-
transform_template(input_file_path, output_file_path) # type: ignore[no-untyped-call]
141+
transform_template(input_file_path, output_file_path)

bin/transform-test-error-json-format.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
It makes error json easier to review by breaking down "errorMessage"
66
into list of strings (delimiter: ". ").
77
"""
8-
import os
98
import sys
9+
from pathlib import Path
1010

1111
from typing_extensions import Final
1212

13-
my_path = os.path.dirname(os.path.abspath(__file__))
14-
sys.path.insert(0, my_path + "/..")
13+
# To allow this script to be executed from other directories
14+
sys.path.insert(0, str(Path(__file__).absolute().parent.parent))
1515

1616
import json
1717
from typing import Type

0 commit comments

Comments
 (0)