Skip to content

Commit 8111630

Browse files
authored
Revert npm dependency (#737)
1 parent 001bb7e commit 8111630

File tree

10 files changed

+35
-274
lines changed

10 files changed

+35
-274
lines changed

README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ This tool can be installed using [pip](https://pypi.org/project/pip/) from the P
1717
```bash
1818
pip install cloudformation-cli cloudformation-cli-java-plugin cloudformation-cli-go-plugin cloudformation-cli-python-plugin cloudformation-cli-typescript-plugin
1919
```
20-
You will need npm to run contract tests (cfn test) if your resource schema requires property transform. Please ensure you have npm installed. Follow the instructions: https://www.npmjs.com/get-npm to get npm installed on your machine.
2120

2221

2322
### Command: init
@@ -103,12 +102,6 @@ If you're creating a resource type, you will also need to install a language plu
103102
pip install -e ../cloudformation-cli-java-plugin
104103
```
105104

106-
Linting and running unit tests is done via [pre-commit](https://pre-commit.com/), and so is performed automatically on commit. The continuous integration also runs these checks. Manual options are available, so you don't have to commit:
107-
108-
These one-time steps are required to ensure all unit tests are passing:
109-
* Ensure npm is installed by [following these steps](https://www.npmjs.com/get-npm)
110-
* Install jsonata: `npm install jsonata`
111-
112105
```bash
113106
# run all hooks on all files, mirrors what the CI runs
114107
pre-commit run --all-files

src/rpdk/core/cli.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ def no_command(args):
106106
# some cases where it makes sense for the commands to raise SystemExit.)
107107
log.debug("Caught exit recommendation", exc_info=e)
108108
log.critical(str(e))
109-
raise SystemExit(1) from e
109+
# pylint: disable=W0707
110+
raise SystemExit(1)
110111
except DownstreamError as e:
111112
# For these operations, we don't want to swallow the exception
112113
log.debug("Caught downstream error", exc_info=e)
@@ -126,8 +127,9 @@ def no_command(args):
126127
"https://github.com/aws-cloudformation/aws-cloudformation-rpdk/issues",
127128
file=sys.stderr,
128129
)
129-
raise SystemExit(2) from e
130-
except Exception as e: # pylint: disable=broad-except
130+
# pylint: disable=W0707
131+
raise SystemExit(2)
132+
except Exception: # pylint: disable=broad-except
131133
print("=== Unhandled exception ===", file=sys.stderr)
132134
print("Please report this issue to the team.", file=sys.stderr)
133135
print(
@@ -143,4 +145,5 @@ def no_command(args):
143145
import traceback # pylint: disable=import-outside-toplevel
144146

145147
traceback.print_exc()
146-
raise SystemExit(EXIT_UNHANDLED_EXCEPTION) from e
148+
# pylint: disable=W0707
149+
raise SystemExit(EXIT_UNHANDLED_EXCEPTION)

src/rpdk/core/contract/resource_client.py

Lines changed: 3 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
# pylint: disable=import-outside-toplevel
22
# pylint: disable=R0904
3-
# have to skip B404, import_subprocess is required for executing typescript
4-
# have to skip B60*, to allow typescript code to be executed using subprocess
53
import json
64
import logging
75
import re
8-
import subprocess # nosec
9-
import tempfile
106
import time
117
from time import sleep
128
from uuid import uuid4
139

1410
import docker
1511
from botocore import UNSIGNED
1612
from botocore.config import Config
17-
from jinja2 import Environment, PackageLoader, select_autoescape
1813

1914
from rpdk.core.contract.interface import Action, HandlerErrorCode, OperationStatus
2015
from rpdk.core.exceptions import InvalidProjectError
@@ -181,13 +176,6 @@ def _properties_to_paths(self, key):
181176

182177
def _update_schema(self, schema):
183178
# TODO: resolve $ref
184-
self.env = Environment(
185-
trim_blocks=True,
186-
lstrip_blocks=True,
187-
keep_trailing_newline=True,
188-
loader=PackageLoader(__name__, "templates/"),
189-
autoescape=select_autoescape(["html", "htm", "xml", "md"]),
190-
)
191179
self._schema = schema
192180
self._strategy = None
193181
self._update_strategy = None
@@ -198,14 +186,12 @@ def _update_schema(self, schema):
198186
self.write_only_paths = self._properties_to_paths("writeOnlyProperties")
199187
self.create_only_paths = self._properties_to_paths("createOnlyProperties")
200188
self.properties_without_insertion_order = self.get_metadata()
201-
self.property_transform_keys = self._properties_to_paths("propertyTransform")
202-
self.property_transform = self._schema.get("propertyTransform")
189+
203190
additional_identifiers = self._schema.get("additionalIdentifiers", [])
204191
self._additional_identifiers_paths = [
205192
{fragment_decode(prop, prefix="") for prop in identifier}
206193
for identifier in additional_identifiers
207194
]
208-
self.transformation_template = self.env.get_template("transformation.template")
209195

210196
def has_only_writable_identifiers(self):
211197
return all(
@@ -233,119 +219,6 @@ def get_metadata(self):
233219
and properties[prop]["insertionOrder"] == "false"
234220
}
235221

236-
def transform_model(self, input_model, output_model): # pylint: disable=R0914
237-
if self.property_transform_keys:
238-
self.check_npm()
239-
for prop in self.property_transform_keys: # pylint: disable=R1702
240-
try:
241-
document_input, _path_input, _parent_input = traverse(
242-
input_model, list(prop)[1:]
243-
)
244-
document_output, _path_output, _parent_output = traverse(
245-
output_model, list(prop)[1:]
246-
)
247-
if not self.compare(document_input, document_output):
248-
path = "/" + "/".join(prop)
249-
property_transform_value = self.property_transform[path].replace(
250-
'"', '\\"'
251-
)
252-
if "$OR" in property_transform_value:
253-
transform_functions = property_transform_value.split("$OR")
254-
for transform_function in transform_functions:
255-
transformed_property = self.transform(
256-
transform_function, input_model
257-
)
258-
value = self.convert_type(
259-
document_input, transformed_property
260-
)
261-
if self.compare(value, document_output):
262-
self.update_transformed_property(
263-
prop, value, input_model
264-
)
265-
else:
266-
transformed_property = self.transform(
267-
property_transform_value, input_model
268-
)
269-
value = self.convert_type(document_input, transformed_property)
270-
self.update_transformed_property(prop, value, input_model)
271-
272-
except KeyError:
273-
pass
274-
275-
@staticmethod
276-
def convert_type(document_input, transformed_property):
277-
if isinstance(document_input, bool):
278-
return bool(transformed_property)
279-
if isinstance(document_input, int):
280-
return int(transformed_property)
281-
if isinstance(document_input, float):
282-
return float(transformed_property)
283-
return transformed_property
284-
285-
@staticmethod
286-
def compare(document_input, document_output):
287-
try:
288-
if isinstance(document_input, str):
289-
return bool(re.match(f"^{document_input}$", document_output))
290-
return document_input == document_output
291-
except re.error:
292-
return document_input == document_output
293-
294-
def transform(self, property_transform_value, input_model):
295-
LOG.debug("This is the transform %s", property_transform_value)
296-
content = self.transformation_template.render(
297-
input_model=input_model, jsonata_expression=property_transform_value
298-
)
299-
LOG.debug("This is the content %s", content)
300-
file = tempfile.NamedTemporaryFile(
301-
mode="w+b",
302-
buffering=-1,
303-
encoding=None,
304-
newline=None,
305-
suffix=".js",
306-
prefix=None,
307-
dir=".",
308-
delete=True,
309-
)
310-
file.write(str.encode(content))
311-
312-
LOG.debug("Jsonata transformation content %s", file.read().decode())
313-
jsonata_output = subprocess.getoutput("node " + file.name)
314-
315-
file.close()
316-
return jsonata_output
317-
318-
# removing coverage for this method as it is not possible to check both npm
319-
# exists and does not exist condition in unit test
320-
@staticmethod
321-
def check_npm(): # pragma: no cover
322-
output = subprocess.getoutput("npm list jsonata")
323-
if "npm: command not found" not in output:
324-
if "jsonata@" not in output:
325-
subprocess.getoutput("npm install jsonata")
326-
else:
327-
err_msg = (
328-
"NPM is required to support propertyTransform. "
329-
"Please install npm using the following link: https://www.npmjs.com/get-npm"
330-
)
331-
LOG.error(err_msg)
332-
raise AssertionError(err_msg)
333-
334-
@staticmethod
335-
def update_transformed_property(property_path, transformed_property, input_model):
336-
try:
337-
_prop, resolved_path, parent = traverse(
338-
input_model, list(property_path)[1:]
339-
)
340-
except LookupError:
341-
LOG.debug(
342-
"Override failed.\nPath %s\nDocument %s", property_path, input_model
343-
)
344-
LOG.warning("Override with path %s not found, skipping", property_path)
345-
else:
346-
key = resolved_path[-1]
347-
parent[key] = transformed_property
348-
349222
@property
350223
def strategy(self):
351224
# an empty strategy (i.e. false-y) is valid
@@ -518,7 +391,7 @@ def make_request(
518391
creds,
519392
token,
520393
callback_context=None,
521-
**kwargs,
394+
**kwargs
522395
):
523396
return {
524397
"requestData": {
@@ -594,7 +467,7 @@ def _make_payload(self, action, current_model, previous_model=None, **kwargs):
594467
self._session, LOWER_CAMEL_CRED_KEYS, self._role_arn
595468
),
596469
self.generate_token(),
597-
**kwargs,
470+
**kwargs
598471
)
599472

600473
def _call(self, payload):

src/rpdk/core/contract/suite/handler_commons.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,14 @@ def test_input_equals_output(resource_client, input_model, output_model):
173173
# only comparing properties in input model to those in output model and
174174
# ignoring extraneous properties that maybe present in output model.
175175
try:
176-
resource_client.transform_model(pruned_input_model, pruned_output_model)
177176
for key in pruned_input_model:
178177
if key in resource_client.properties_without_insertion_order:
179178
assert test_unordered_list_match(
180179
pruned_input_model[key], pruned_output_model[key]
181180
)
182181
else:
183-
assert resource_client.compare(
184-
pruned_input_model[key], pruned_output_model[key]
182+
assert (
183+
pruned_input_model[key] == pruned_output_model[key]
185184
), assertion_error_message
186185
except KeyError as e:
187186
raise AssertionError(assertion_error_message) from e

src/rpdk/core/contract/templates/transformation.template

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/rpdk/core/init.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ def __init__(self, choices):
7171
def __call__(self, value):
7272
try:
7373
choice = int(value)
74-
except ValueError as value_error:
75-
raise WizardValidationError("Please enter an integer") from value_error
74+
except ValueError:
75+
# pylint: disable=W0707
76+
raise WizardValidationError("Please enter an integer")
7677
choice -= 1
7778
if choice < 0 or choice >= self.max:
7879
raise WizardValidationError("Please select a choice")
@@ -148,9 +149,10 @@ def ignore_abort(function):
148149
def wrapper(args):
149150
try:
150151
function(args)
151-
except (KeyboardInterrupt, WizardAbortError) as exception:
152+
except (KeyboardInterrupt, WizardAbortError):
152153
print("\naborted")
153-
raise SystemExit(1) from exception
154+
# pylint: disable=W0707
155+
raise SystemExit(1)
154156

155157
return wrapper
156158

src/rpdk/core/jsonutils/flattener.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ def _flatten_ref_type(self, ref_path):
7979
try:
8080
ref_parts = fragment_decode(ref_path)
8181
except ValueError as e:
82+
# pylint: disable=W0707
8283
raise FlatteningError(
8384
"Invalid ref at path '{}': {}".format(ref_path, str(e))
84-
) from e
85+
)
8586

8687
ref_schema, ref_parts, _ref_parent = self._find_subschema_by_ref(ref_parts)
8788
return self._walk(ref_schema, ref_parts)
@@ -185,5 +186,6 @@ def _find_subschema_by_ref(self, ref_path):
185186
"""
186187
try:
187188
return traverse(self._full_schema, ref_path)
188-
except (LookupError, ValueError) as e:
189-
raise FlatteningError("Invalid ref: {}".format(ref_path)) from e
189+
except (LookupError, ValueError):
190+
# pylint: disable=W0707
191+
raise FlatteningError("Invalid ref: {}".format(ref_path))

src/rpdk/core/jsonutils/utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def schema_merge(target, src, path): # noqa: C901 # pylint: disable=R0912
189189
next_path = path + (key,)
190190
try:
191191
target[key] = schema_merge(target_schema, src_schema, next_path)
192-
except TypeError as type_error:
192+
except TypeError:
193193
if key in (TYPE, REF): # combining multiple $ref and types
194194
src_set = to_set(src_schema)
195195

@@ -222,8 +222,7 @@ def schema_merge(target, src, path): # noqa: C901 # pylint: disable=R0912
222222
"Object at path '{path}' declared multiple values "
223223
"for '{}': found '{}' and '{}'"
224224
)
225-
raise ConstraintError(
226-
msg, path, key, target_schema, src_schema
227-
) from type_error
225+
# pylint: disable=W0707
226+
raise ConstraintError(msg, path, key, target_schema, src_schema)
228227
target[key] = src_schema
229228
return target

src/rpdk/core/upload.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,15 @@ def _get_stack_output(self, stack_id, output_key):
7373
for output in outputs
7474
if output["OutputKey"] == output_key
7575
)
76-
except StopIteration as stop_iteration:
76+
except StopIteration:
7777
LOG.debug(
7878
"Outputs from stack '%s' did not contain '%s':\n%s",
7979
stack_id,
8080
output_key,
8181
", ".join(output["OutputKey"] for output in outputs),
8282
)
83-
raise InternalError(
84-
"Required output not found on stack"
85-
) from stop_iteration
83+
# pylint: disable=W0707
84+
raise InternalError("Required output not found on stack")
8685

8786
def _create_or_update_stack(self, template, stack_name):
8887
args = {"StackName": stack_name, "TemplateBody": template}
@@ -141,14 +140,15 @@ def create_or_update_role(self, template_path, resource_type):
141140
try:
142141
with template_path.open("r", encoding="utf-8") as f:
143142
template = f.read()
144-
except FileNotFoundError as file_not_found:
143+
except FileNotFoundError:
145144
LOG.critical(
146145
"CloudFormation template 'resource-role.yaml' "
147146
"for execution role not found. "
148147
"Please run `generate` or "
149148
"provide an execution role via the --role-arn parameter."
150149
)
151-
raise InvalidProjectError() from file_not_found
150+
# pylint: disable=W0707
151+
raise InvalidProjectError()
152152
stack_id = self._create_or_update_stack(
153153
template, "{}-role-stack".format(resource_type)
154154
)

0 commit comments

Comments
 (0)