|
2 | 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3 | 3 | SPDX-License-Identifier: MIT-0
|
4 | 4 | """
|
5 |
| -import logging |
6 |
| -from ast import Call |
7 |
| -from json.decoder import JSONDecodeError |
8 |
| -from typing import Callable, List, Optional, Tuple, Union |
9 | 5 |
|
10 |
| -from yaml import YAMLError |
11 |
| -from yaml.parser import ParserError |
12 |
| -from yaml.scanner import ScannerError |
13 |
| - |
14 |
| -from cfnlint.decode import cfn_json, cfn_yaml |
15 |
| -from cfnlint.rules import Match, ParseError |
16 |
| - |
17 |
| -LOGGER = logging.getLogger(__name__) |
18 |
| - |
19 |
| -Matches = List[Match] |
20 |
| -Decode = Tuple[Union[str, None], Matches] |
21 |
| - |
22 |
| - |
23 |
| -def decode_str(s: str) -> Decode: |
24 |
| - """Decode the string s into an object.""" |
25 |
| - return _decode(cfn_yaml.loads, cfn_json.loads, s, None) |
26 |
| - |
27 |
| - |
28 |
| -def decode(filename: str) -> Decode: |
29 |
| - """Decode filename into an object.""" |
30 |
| - return _decode(cfn_yaml.load, cfn_json.load, filename, filename) |
31 |
| - |
32 |
| - |
33 |
| -def _decode( |
34 |
| - yaml_f: Callable, json_f: Callable, payload: str, filename: Optional[str] |
35 |
| -) -> Decode: |
36 |
| - """Decode payload using yaml_f and json_f, using filename for log output.""" |
37 |
| - template = None |
38 |
| - matches = [] |
39 |
| - try: |
40 |
| - template = yaml_f(payload) |
41 |
| - except IOError as e: |
42 |
| - if e.errno == 2: |
43 |
| - LOGGER.error("Template file not found: %s", filename) |
44 |
| - matches.append( |
45 |
| - create_match_file_error( |
46 |
| - filename, f"Template file not found: {filename}" |
47 |
| - ) |
48 |
| - ) |
49 |
| - elif e.errno == 21: |
50 |
| - LOGGER.error("Template references a directory, not a file: %s", filename) |
51 |
| - matches.append( |
52 |
| - create_match_file_error( |
53 |
| - filename, "Template references a directory, not a file: {filename}" |
54 |
| - ) |
55 |
| - ) |
56 |
| - elif e.errno == 13: |
57 |
| - LOGGER.error("Permission denied when accessing template file: %s", filename) |
58 |
| - matches.append( |
59 |
| - create_match_file_error( |
60 |
| - filename, |
61 |
| - "Permission denied when accessing template file: {filename}", |
62 |
| - ) |
63 |
| - ) |
64 |
| - |
65 |
| - if matches: |
66 |
| - return (None, matches) |
67 |
| - except UnicodeDecodeError as _: |
68 |
| - LOGGER.error("Cannot read file contents: %s", filename) |
69 |
| - matches.append( |
70 |
| - create_match_file_error(filename, "Cannot read file contents: {filename}") |
71 |
| - ) |
72 |
| - except cfn_yaml.CfnParseError as err: |
73 |
| - matches = err.matches |
74 |
| - except ParserError as err: |
75 |
| - matches = [create_match_yaml_parser_error(err, filename)] |
76 |
| - except ScannerError as err: |
77 |
| - if err.problem and ( |
78 |
| - err.problem |
79 |
| - in [ |
80 |
| - "found character '\\t' that cannot start any token", |
81 |
| - "found unknown escape character", |
82 |
| - ] |
83 |
| - or err.problem.startswith("found unknown escape character") |
84 |
| - ): |
85 |
| - try: |
86 |
| - template = json_f(payload) |
87 |
| - except cfn_json.JSONDecodeError as json_errs: |
88 |
| - for json_err in json_errs.matches: |
89 |
| - json_err.filename = filename |
90 |
| - matches = json_errs.matches |
91 |
| - except JSONDecodeError as json_err: |
92 |
| - if hasattr(json_err, "msg"): |
93 |
| - if ( |
94 |
| - json_err.msg == "No JSON object could be decoded" |
95 |
| - ): # pylint: disable=no-member |
96 |
| - matches = [create_match_yaml_parser_error(err, filename)] |
97 |
| - else: |
98 |
| - matches = [create_match_json_parser_error(json_err, filename)] |
99 |
| - if hasattr(json_err, "msg"): |
100 |
| - if json_err.msg == "Expecting value": # pylint: disable=no-member |
101 |
| - matches = [create_match_yaml_parser_error(err, filename)] |
102 |
| - else: |
103 |
| - matches = [create_match_json_parser_error(json_err, filename)] |
104 |
| - except Exception as json_err: # pylint: disable=W0703 |
105 |
| - LOGGER.error("Template %s is malformed: %s", filename, err.problem) |
106 |
| - LOGGER.error( |
107 |
| - "Tried to parse %s as JSON but got error: %s", |
108 |
| - filename, |
109 |
| - str(json_err), |
110 |
| - ) |
111 |
| - return ( |
112 |
| - None, |
113 |
| - [ |
114 |
| - create_match_file_error( |
115 |
| - filename, |
116 |
| - f"Tried to parse {filename} as JSON but got error: {str(json_err)}", |
117 |
| - ) |
118 |
| - ], |
119 |
| - ) |
120 |
| - else: |
121 |
| - matches = [create_match_yaml_parser_error(err, filename)] |
122 |
| - except YAMLError as err: |
123 |
| - matches = [create_match_file_error(filename, err)] |
124 |
| - |
125 |
| - if not isinstance(template, dict) and not matches: |
126 |
| - # Template isn't a dict which means nearly nothing will work |
127 |
| - matches = [ |
128 |
| - Match( |
129 |
| - 1, |
130 |
| - 1, |
131 |
| - 1, |
132 |
| - 1, |
133 |
| - filename, |
134 |
| - ParseError(), |
135 |
| - message="Template needs to be an object.", |
136 |
| - ) |
137 |
| - ] |
138 |
| - return (template, matches) |
139 |
| - |
140 |
| - |
141 |
| -def create_match_yaml_parser_error(parser_error, filename): |
142 |
| - """Create a Match for a parser error""" |
143 |
| - lineno = parser_error.problem_mark.line + 1 |
144 |
| - colno = parser_error.problem_mark.column + 1 |
145 |
| - msg = parser_error.problem |
146 |
| - return Match(lineno, colno, lineno, colno + 1, filename, ParseError(), message=msg) |
147 |
| - |
148 |
| - |
149 |
| -def create_match_file_error(filename, msg): |
150 |
| - """Create a Match for a parser error""" |
151 |
| - return Match( |
152 |
| - linenumber=1, |
153 |
| - columnnumber=1, |
154 |
| - linenumberend=1, |
155 |
| - columnnumberend=2, |
156 |
| - filename=filename, |
157 |
| - rule=ParseError(), |
158 |
| - message=msg, |
159 |
| - ) |
160 |
| - |
161 |
| - |
162 |
| -def create_match_json_parser_error(parser_error, filename): |
163 |
| - """Create a Match for a parser error""" |
164 |
| - lineno = parser_error.lineno |
165 |
| - colno = parser_error.colno |
166 |
| - msg = parser_error.msg |
167 |
| - return Match(lineno, colno, lineno, colno + 1, filename, ParseError(), message=msg) |
| 6 | +from cfnlint.decode.decode import ( |
| 7 | + create_match_file_error, |
| 8 | + create_match_json_parser_error, |
| 9 | + create_match_yaml_parser_error, |
| 10 | + decode, |
| 11 | + decode_str, |
| 12 | +) |
0 commit comments