Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
repos:
- repo: https://github.com/python/black
rev: 19.3b0
hooks:
- id: black
language_version: python3.7
exclude_types: ['markdown', 'ini', 'toml', 'rst']
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ matrix:

install:
# Install the code requirements
- mkdir $HOME/bin-black
- wget -O $HOME/bin-black/black https://github.com/python/black/releases/download/19.3b0/black
- chmod +x $HOME/bin-black/black
- export PATH=$PATH:$HOME/bin-black
- black --version

- make init

# Install Docs requirements
Expand Down
19 changes: 17 additions & 2 deletions DEVELOPMENT_GUIDE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,23 @@ Setup Python locally using `pyenv`_
#. ``pyenv install 2.7.14``
#. Make the Python version available in the project: ``pyenv local 2.7.14``

2. Install Additional Tooling
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Black
~~~~~~~~
We format our code using [Black](https://github.com/python/black) and verify the source code is black compliant
in Appveyor during PRs. You can find installation instructions on [Black's docs](https://black.readthedocs.io/en/stable/installation_and_usage.html).

2. Activate Virtualenv
After installing, you can run our formatting through our Makefile by `make black-format` or integrating Black directly in your favorite IDE (instructions
can be found [here](https://black.readthedocs.io/en/stable/editor_integration.html))

Pre-commit
~~~~~~~~~~
If you don't wish to manually run black on each pr or install black manually, we have integrated black into git hooks through [pre-commit](https://pre-commit.com/).
After installing pre-commit, run `pre-commit install` in the root of the project. This will install black for you and run the black formatting on
commit.

3. Activate Virtualenv
~~~~~~~~~~~~~~~~~~~~~~
Virtualenv allows you to install required libraries outside of the Python installation. A good practice is to setup
a different virtualenv for each project. `pyenv`_ comes with a handy plugin that can create virtualenv.
Expand All @@ -37,7 +52,7 @@ a different virtualenv for each project. `pyenv`_ comes with a handy plugin that
#. [Optional] Automatically activate the virtualenv in for this folder: ``pyenv local samtranslator27``


3. Install dependencies
4. Install dependencies
~~~~~~~~~~~~~~~~~~~~~~~
Install dependencies by running the following command. Make sure the Virtualenv you created above is active.

Expand Down
15 changes: 8 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,21 @@ init:
$(info [*] Install requirements...)
@pip install -r requirements/dev.txt -r requirements/base.txt

flake:
$(info [*] Running flake8...)
@flake8 samtranslator

test:
$(info [*] Run the unit test with minimum code coverage of $(CODE_COVERAGE)%...)
@pytest --cov samtranslator --cov-report term-missing --cov-fail-under $(CODE_COVERAGE) tests

black:
black setup.py samtranslator/* tests/* bin/*

black-check:
black --check setup.py samtranslator/* tests/* bin/*

# Command to run everytime you make changes to verify everything works
dev: flake test
dev: test

# Verifications to run before sending a pull request
pr: init dev
pr: black-check init dev

define HELP_MESSAGE

Expand All @@ -64,7 +66,6 @@ TARGETS
init Initialize and install the requirements and dev-requirements for this project.
test Run the Unit tests.
dev Run all development tests after a change.
build-docs Generate the documentation.
pr Perform all checks before submitting a Pull Request.

endef
64 changes: 28 additions & 36 deletions bin/sam-translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from docopt import docopt

my_path = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, my_path + '/..')
sys.path.insert(0, my_path + "/..")

from samtranslator.public.translator import ManagedPolicyLoader
from samtranslator.translator.transform import transform
Expand All @@ -38,18 +38,19 @@

LOG = logging.getLogger(__name__)
cli_options = docopt(__doc__)
iam_client = boto3.client('iam')
iam_client = boto3.client("iam")
cwd = os.getcwd()

if cli_options.get('--verbose'):
if cli_options.get("--verbose"):
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig()


def execute_command(command, args):
try:
aws_cmd = 'aws' if platform.system().lower() != 'windows' else 'aws.cmd'
command_with_args = [aws_cmd, 'cloudformation', command] + list(args)
aws_cmd = "aws" if platform.system().lower() != "windows" else "aws.cmd"
command_with_args = [aws_cmd, "cloudformation", command] + list(args)

LOG.debug("Executing command: %s", command_with_args)

Expand All @@ -63,8 +64,8 @@ def execute_command(command, args):


def get_input_output_file_paths():
input_file_option = cli_options.get('--template-file')
output_file_option = cli_options.get('--output-template')
input_file_option = cli_options.get("--template-file")
output_file_option = cli_options.get("--output-template")
input_file_path = os.path.join(cwd, input_file_option)
output_file_path = os.path.join(cwd, output_file_option)

Expand All @@ -73,67 +74,58 @@ def get_input_output_file_paths():

def package(input_file_path, output_file_path):
template_file = input_file_path
package_output_template_file = input_file_path + '._sam_packaged_.yaml'
s3_bucket = cli_options.get('--s3-bucket')
package_output_template_file = input_file_path + "._sam_packaged_.yaml"
s3_bucket = cli_options.get("--s3-bucket")
args = [
'--template-file',
"--template-file",
template_file,
'--output-template-file',
"--output-template-file",
package_output_template_file,
'--s3-bucket',
s3_bucket
"--s3-bucket",
s3_bucket,
]

execute_command('package', args)
execute_command("package", args)

return package_output_template_file


def transform_template(input_file_path, output_file_path):
with open(input_file_path, 'r') as f:
with open(input_file_path, "r") as f:
sam_template = yaml_parse(f)

try:
cloud_formation_template = transform(
sam_template, {}, ManagedPolicyLoader(iam_client))
cloud_formation_template_prettified = json.dumps(
cloud_formation_template, indent=2)
cloud_formation_template = transform(sam_template, {}, ManagedPolicyLoader(iam_client))
cloud_formation_template_prettified = json.dumps(cloud_formation_template, indent=2)

with open(output_file_path, 'w') as f:
with open(output_file_path, "w") as f:
f.write(cloud_formation_template_prettified)

print('Wrote transformed CloudFormation template to: ' + output_file_path)
print ("Wrote transformed CloudFormation template to: " + output_file_path)
except InvalidDocumentException as e:
errorMessage = reduce(lambda message, error: message + ' ' + error.message, e.causes, e.message)
errorMessage = reduce(lambda message, error: message + " " + error.message, e.causes, e.message)
LOG.error(errorMessage)
errors = map(lambda cause: cause.message, e.causes)
LOG.error(errors)


def deploy(template_file):
capabilities = cli_options.get('--capabilities')
stack_name = cli_options.get('--stack-name')
args = [
'--template-file',
template_file,
'--capabilities',
capabilities,
'--stack-name',
stack_name
]
capabilities = cli_options.get("--capabilities")
stack_name = cli_options.get("--stack-name")
args = ["--template-file", template_file, "--capabilities", capabilities, "--stack-name", stack_name]

execute_command('deploy', args)
execute_command("deploy", args)

return package_output_template_file


if __name__ == '__main__':
if __name__ == "__main__":
input_file_path, output_file_path = get_input_output_file_paths()

if cli_options.get('package'):
if cli_options.get("package"):
package_output_template_file = package(input_file_path, output_file_path)
transform_template(package_output_template_file, output_file_path)
elif cli_options.get('deploy'):
elif cli_options.get("deploy"):
package_output_template_file = package(input_file_path, output_file_path)
transform_template(package_output_template_file, output_file_path)
deploy(output_file_path)
Expand Down
18 changes: 18 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[tool.black]
line-length = 120
target_version = ['py27', 'py37', 'py36', 'py38']
exclude = '''

(
/(
\.eggs # exclude a few common directories in the
| \.git # root of the project
| \.tox
| \.venv
| build
| dist
| pip-wheel-metadata
| examples
)/
)
'''
2 changes: 1 addition & 1 deletion samtranslator/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.20.0'
__version__ = "1.20.0"
67 changes: 40 additions & 27 deletions samtranslator/intrinsics/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ def can_handle(self, input_dict):
:return: True if it matches expected structure, False otherwise
"""

return input_dict is not None \
and isinstance(input_dict, dict) \
and len(input_dict) == 1 \
return (
input_dict is not None
and isinstance(input_dict, dict)
and len(input_dict) == 1
and self.intrinsic_name in input_dict
)

@classmethod
def _parse_resource_reference(cls, ref_value):
Expand Down Expand Up @@ -132,9 +134,7 @@ def resolve_resource_refs(self, input_dict, supported_resource_refs):
if not resolved_value:
return input_dict

return {
self.intrinsic_name: resolved_value
}
return {self.intrinsic_name: resolved_value}

def resolve_resource_id_refs(self, input_dict, supported_resource_id_refs):
"""
Expand All @@ -161,9 +161,7 @@ def resolve_resource_id_refs(self, input_dict, supported_resource_id_refs):
if not resolved_value:
return input_dict

return {
self.intrinsic_name: resolved_value
}
return {self.intrinsic_name: resolved_value}


class SubAction(Action):
Expand Down Expand Up @@ -372,16 +370,18 @@ def handler_method(full_ref, ref_value):
"""

# RegExp to find pattern "${logicalId.property}" and return the word inside bracket
logical_id_regex = '[A-Za-z0-9\.]+|AWS::[A-Z][A-Za-z]*'
ref_pattern = re.compile(r'\$\{(' + logical_id_regex + ')\}')
logical_id_regex = "[A-Za-z0-9\.]+|AWS::[A-Z][A-Za-z]*"
ref_pattern = re.compile(r"\$\{(" + logical_id_regex + ")\}")

# Find all the pattern, and call the handler to decide how to substitute them.
# Do the substitution and return the final text
return re.sub(ref_pattern,
# Pass the handler entire string ${logicalId.property} as first parameter and "logicalId.property"
# as second parameter. Return value will be substituted
lambda match: handler_method(match.group(0), match.group(1)),
text)
return re.sub(
ref_pattern,
# Pass the handler entire string ${logicalId.property} as first parameter and "logicalId.property"
# as second parameter. Return value will be substituted
lambda match: handler_method(match.group(0), match.group(1)),
text,
)


class GetAttAction(Action):
Expand Down Expand Up @@ -427,10 +427,14 @@ def resolve_resource_refs(self, input_dict, supported_resource_refs):
if not isinstance(value, list) or len(value) < 2:
return input_dict

if (not all(isinstance(entry, string_types) for entry in value)):
if not all(isinstance(entry, string_types) for entry in value):
raise InvalidDocumentException(
[InvalidTemplateException('Invalid GetAtt value {}. GetAtt expects an array with 2 strings.'
.format(value))])
[
InvalidTemplateException(
"Invalid GetAtt value {}. GetAtt expects an array with 2 strings.".format(value)
)
]
)

# Value of GetAtt is an array. It can contain any number of elements, with first being the LogicalId of
# resource and rest being the attributes. In a SAM template, a reference to a resource can be used in the
Expand Down Expand Up @@ -515,6 +519,7 @@ class FindInMapAction(Action):
"""
This action can't be used along with other actions.
"""

intrinsic_name = "Fn::FindInMap"

def resolve_parameter_refs(self, input_dict, parameters):
Expand All @@ -535,21 +540,29 @@ def resolve_parameter_refs(self, input_dict, parameters):
# FindInMap expects an array with 3 values
if not isinstance(value, list) or len(value) != 3:
raise InvalidDocumentException(
[InvalidTemplateException('Invalid FindInMap value {}. FindInMap expects an array with 3 values.'
.format(value))])
[
InvalidTemplateException(
"Invalid FindInMap value {}. FindInMap expects an array with 3 values.".format(value)
)
]
)

map_name = self.resolve_parameter_refs(value[0], parameters)
top_level_key = self.resolve_parameter_refs(value[1], parameters)
second_level_key = self.resolve_parameter_refs(value[2], parameters)

if not isinstance(map_name, string_types) or \
not isinstance(top_level_key, string_types) or \
not isinstance(second_level_key, string_types):
if (
not isinstance(map_name, string_types)
or not isinstance(top_level_key, string_types)
or not isinstance(second_level_key, string_types)
):
return input_dict

if map_name not in parameters or \
top_level_key not in parameters[map_name] or \
second_level_key not in parameters[map_name][top_level_key]:
if (
map_name not in parameters
or top_level_key not in parameters[map_name]
or second_level_key not in parameters[map_name][top_level_key]
):
return input_dict

return parameters[map_name][top_level_key][second_level_key]
Loading