python-hcl2 4.3.5__tar.gz → 5.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/.gitignore +3 -0
  2. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/PKG-INFO +1 -1
  3. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/hcl2/__init__.py +1 -1
  4. python-hcl2-5.0.0/hcl2/api.py +69 -0
  5. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/hcl2/hcl2.lark +10 -6
  6. python-hcl2-5.0.0/hcl2/reconstructor.py +162 -0
  7. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/hcl2/transformer.py +5 -2
  8. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/hcl2/version.py +2 -2
  9. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/python_hcl2.egg-info/PKG-INFO +1 -1
  10. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/python_hcl2.egg-info/SOURCES.txt +1 -0
  11. python-hcl2-4.3.5/hcl2/api.py +0 -28
  12. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/.codacy.yml +0 -0
  13. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/.coveragerc +0 -0
  14. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/.github/CODEOWNERS +0 -0
  15. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/.github/workflows/codeql-analysis.yml +0 -0
  16. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/.github/workflows/pr_check.yml +0 -0
  17. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/.github/workflows/publish.yml +0 -0
  18. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/.pre-commit-config.yaml +0 -0
  19. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/.yamllint.yml +0 -0
  20. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/CHANGELOG.md +0 -0
  21. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/LICENSE +0 -0
  22. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/MANIFEST.in +0 -0
  23. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/README.md +0 -0
  24. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/bin/terraform_test +0 -0
  25. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/hcl2/__main__.py +0 -0
  26. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/hcl2/parser.py +0 -0
  27. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/hcl2/py.typed +0 -0
  28. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/mypy.ini +0 -0
  29. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/pylintrc +0 -0
  30. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/pyproject.toml +0 -0
  31. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/python_hcl2.egg-info/dependency_links.txt +0 -0
  32. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/python_hcl2.egg-info/entry_points.txt +0 -0
  33. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/python_hcl2.egg-info/not-zip-safe +0 -0
  34. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/python_hcl2.egg-info/requires.txt +0 -0
  35. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/python_hcl2.egg-info/top_level.txt +0 -0
  36. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/reports/.gitignore +0 -0
  37. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/requirements.txt +0 -0
  38. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/setup.cfg +0 -0
  39. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/test-requirements.txt +0 -0
  40. {python-hcl2-4.3.5 → python-hcl2-5.0.0}/tox.ini +0 -0
@@ -121,3 +121,6 @@ node_modules/
121
121
  # Don't commit the generated parser
122
122
  lark_parser.py
123
123
  .lark_cache.bin
124
+
125
+ # ASDF tool-versions file
126
+ .tool-versions
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-hcl2
3
- Version: 4.3.5
3
+ Version: 5.0.0
4
4
  Summary: A parser for HCL2
5
5
  Author-email: Amplify Education <github@amplify.com>
6
6
  License: MIT
@@ -5,4 +5,4 @@ try:
5
5
  except ImportError:
6
6
  __version__ = "unknown"
7
7
 
8
- from .api import load, loads
8
+ from .api import load, loads, parse, parses, transform, writes, AST
@@ -0,0 +1,69 @@
1
+ """The API that will be exposed to users of this package"""
2
+ from typing import TextIO
3
+
4
+ from lark.tree import Tree as AST
5
+ from hcl2.parser import hcl2
6
+ from hcl2.transformer import DictTransformer
7
+
8
+
9
+ def load(file: TextIO, with_meta=False) -> dict:
10
+ """Load a HCL2 file.
11
+ :param file: File with hcl2 to be loaded as a dict.
12
+ :param with_meta: If set to true then adds `__start_line__` and `__end_line__`
13
+ parameters to the output dict. Default to false.
14
+ """
15
+ return loads(file.read(), with_meta=with_meta)
16
+
17
+
18
+ def loads(text: str, with_meta=False) -> dict:
19
+ """Load HCL2 from a string.
20
+ :param text: Text with hcl2 to be loaded as a dict.
21
+ :param with_meta: If set to true then adds `__start_line__` and `__end_line__`
22
+ parameters to the output dict. Default to false.
23
+ """
24
+ # append new line as a workaround for https://github.com/lark-parser/lark/issues/237
25
+ # Lark doesn't support a EOF token so our grammar can't look for "new line or end of file"
26
+ # This means that all blocks must end in a new line even if the file ends
27
+ # Append a new line as a temporary fix
28
+ tree = hcl2.parse(text + "\n")
29
+ return DictTransformer(with_meta=with_meta).transform(tree)
30
+
31
+
32
+ def parse(file: TextIO) -> AST:
33
+ """Load HCL2 syntax tree from a file.
34
+ :param file: File with hcl2 to be loaded as a dict.
35
+ """
36
+ return parses(file.read())
37
+
38
+
39
+ def parses(text: str) -> AST:
40
+ """Load HCL2 syntax tree from a string.
41
+ :param text: Text with hcl2 to be loaded as a dict.
42
+ """
43
+ # defer this import until this method is called, due to the performance hit
44
+ # of rebuilding the grammar without cache
45
+ from hcl2.reconstructor import ( # pylint: disable=import-outside-toplevel
46
+ hcl2 as uncached_hcl2,
47
+ )
48
+
49
+ return uncached_hcl2.parse(text)
50
+
51
+
52
+ def transform(ast: AST, with_meta=False) -> dict:
53
+ """Convert an HCL2 AST to a dictionary.
54
+ :param ast: HCL2 syntax tree, output from `parse` or `parses`
55
+ """
56
+ return DictTransformer(with_meta=with_meta).transform(ast)
57
+
58
+
59
+ def writes(ast: AST) -> str:
60
+ """Convert an HCL2 syntax tree to a string.
61
+ :param ast: HCL2 syntax tree, output from `parse` or `parses`
62
+ """
63
+ # defer this import until this method is called, due to the performance hit
64
+ # of rebuilding the grammar without cache
65
+ from hcl2.reconstructor import ( # pylint: disable=import-outside-toplevel
66
+ hcl2_reconstructor,
67
+ )
68
+
69
+ return hcl2_reconstructor.reconstruct(ast)
@@ -1,11 +1,13 @@
1
1
  start : body
2
2
  body : (new_line_or_comment? (attribute | block))* new_line_or_comment?
3
- attribute : identifier "=" expression
3
+ attribute : identifier EQ expression
4
4
  block : identifier (identifier | STRING_LIT)* new_line_or_comment? "{" body "}"
5
5
  new_line_and_or_comma: new_line_or_comment | "," | "," new_line_or_comment
6
- new_line_or_comment: ( /\n/ | /#.*\n/ | /\/\/.*\n/ )+
6
+ new_line_or_comment: ( NL_OR_COMMENT )+
7
+ NL_OR_COMMENT: /\n[ \t]*/ | /#.*\n/ | /\/\/.*\n/ | /\/\*(.|\n)*?(\*\/)/
7
8
 
8
- identifier : /[a-zA-Z_][a-zA-Z0-9_-]*/ | IN | FOR | IF | FOR_EACH
9
+ identifier : NAME | IN | FOR | IF | FOR_EACH
10
+ NAME : /[a-zA-Z_][a-zA-Z0-9_-]*/
9
11
  IF : "if"
10
12
  IN : "in"
11
13
  FOR : "for"
@@ -18,8 +20,9 @@ conditional : expression "?" new_line_or_comment? expression new_line_or_comment
18
20
  ?operation : unary_op | binary_op
19
21
  !unary_op : ("-" | "!") expr_term
20
22
  binary_op : expression binary_term new_line_or_comment?
21
- !binary_operator : "==" | "!=" | "<" | ">" | "<=" | ">=" | "-" | "*" | "/" | "%" | "&&" | "||" | "+"
23
+ !binary_operator : BINARY_OP
22
24
  binary_term : binary_operator new_line_or_comment? expression
25
+ BINARY_OP : "==" | "!=" | "<" | ">" | "<=" | ">=" | "-" | "*" | "/" | "%" | "&&" | "||" | "+"
23
26
 
24
27
  expr_term : "(" new_line_or_comment? expression new_line_or_comment? ")"
25
28
  | float_lit
@@ -50,10 +53,12 @@ int_lit : DECIMAL+
50
53
  | DECIMAL+ ("." DECIMAL+)? EXP_MARK DECIMAL+
51
54
  DECIMAL : "0".."9"
52
55
  EXP_MARK : ("e" | "E") ("+" | "-")?
56
+ EQ : /[ \t]*=(?!=|>)/
53
57
 
54
58
  tuple : "[" (new_line_or_comment* expression new_line_or_comment* ",")* (new_line_or_comment* expression)? new_line_or_comment* "]"
55
59
  object : "{" new_line_or_comment? (object_elem (new_line_and_or_comma object_elem )* new_line_and_or_comma?)? "}"
56
- object_elem : (identifier | expression) ("=" | ":") expression
60
+ object_elem : (identifier | expression) ( EQ | ":") expression
61
+
57
62
 
58
63
  heredoc_template : /<<(?P<heredoc>[a-zA-Z][a-zA-Z0-9._-]+)\n(?:.|\n)*?(?P=heredoc)/
59
64
  heredoc_template_trim : /<<-(?P<heredoc_trim>[a-zA-Z][a-zA-Z0-9._-]+)\n(?:.|\n)*?(?P=heredoc_trim)/
@@ -78,4 +83,3 @@ full_splat : "[*]" (get_attr | index)*
78
83
  !for_cond : "if" new_line_or_comment? expression
79
84
 
80
85
  %ignore /[ \t]+/
81
- %ignore /\/\*(.|\n)*?(\*\/)/
@@ -0,0 +1,162 @@
1
+ """A reconstructor for HCL2 implemented using Lark's experimental reconstruction functionality"""
2
+
3
+ from lark import Lark
4
+ from lark.reconstruct import Reconstructor
5
+ from lark.utils import is_id_continue
6
+
7
+ # this is duplicated from `parser` because we need different options here for
8
+ # the reconstructor. please make sure changes are kept in sync between the two
9
+ # if necessary.
10
+ hcl2 = Lark.open(
11
+ "hcl2.lark",
12
+ parser="lalr",
13
+ # Caching must be disabled to allow for reconstruction until lark-parser/lark#1472 is fixed:
14
+ #
15
+ # https://github.com/lark-parser/lark/issues/1472
16
+ #
17
+ # cache=str(PARSER_FILE), # Disable/Delete file to effect changes to the grammar
18
+ rel_to=__file__,
19
+ propagate_positions=True,
20
+ maybe_placeholders=False, # Needed for reconstruction
21
+ )
22
+
23
+ CHAR_SPACE_AFTER = set(',~@<>="|?)]:')
24
+ CHAR_SPACE_BEFORE = (CHAR_SPACE_AFTER - set(",=")) | set("'")
25
+ KEYWORDS_SPACE_AFTER = [
26
+ "if",
27
+ "in",
28
+ "for",
29
+ "for_each",
30
+ "==",
31
+ "!=",
32
+ "<",
33
+ ">",
34
+ "<=",
35
+ ">=",
36
+ "-",
37
+ "*",
38
+ "/",
39
+ "%",
40
+ "&&",
41
+ "||",
42
+ "+",
43
+ ]
44
+ KEYWORDS_SPACE_BEFORE = KEYWORDS_SPACE_AFTER
45
+ DIGITS = set("0123456789")
46
+ NEVER_SPACE_AFTER = set("[(")
47
+ NEVER_SPACE_BEFORE = set("]),.")
48
+ NEVER_COMMA_BEFORE = set("])}")
49
+ # characters that are OK to come right after an identifier with no space between
50
+ IDENT_NO_SPACE = set("()[]")
51
+
52
+
53
+ def _add_extra_space(prev_item, item):
54
+ # pylint: disable=too-many-boolean-expressions, too-many-return-statements
55
+
56
+ ##### the scenarios where explicitly disallow spaces: #####
57
+
58
+ # if we already have a space, don't add another
59
+ if prev_item[-1].isspace() or item[0].isspace():
60
+ return False
61
+
62
+ # none of the following should be separated by spaces:
63
+ # - groups of digits
64
+ # - namespaced::function::calls
65
+ # - characters within an identifier like array[0]()
66
+ if (
67
+ (prev_item[-1] in DIGITS and item[0] in DIGITS)
68
+ or item == "::"
69
+ or prev_item == "::"
70
+ or (prev_item[-1] in IDENT_NO_SPACE and item[0] in IDENT_NO_SPACE)
71
+ ):
72
+ return False
73
+
74
+ # specific characters are also blocklisted from having spaces
75
+ if prev_item[-1] in NEVER_SPACE_AFTER or item[0] in NEVER_SPACE_BEFORE:
76
+ return False
77
+
78
+ ##### the scenarios where we add spaces: #####
79
+
80
+ # scenario 1, the prev token ended with an identifier character
81
+ # and the next character is not an "IDENT_NO_SPACE" character
82
+ if is_id_continue(prev_item[-1]) and not item[0] in IDENT_NO_SPACE:
83
+ return True
84
+
85
+ # scenario 2, the prev token or the next token should be followed by a space
86
+ if (
87
+ prev_item[-1] in CHAR_SPACE_AFTER
88
+ or prev_item in KEYWORDS_SPACE_AFTER
89
+ or item[0] in CHAR_SPACE_BEFORE
90
+ or item in KEYWORDS_SPACE_BEFORE
91
+ ):
92
+ return True
93
+
94
+ # scenario 3, the previous token was a block opening brace and
95
+ # the next token is not a closing brace (so the block is on one
96
+ # line and not empty)
97
+ if prev_item[-1] == "{" and item[0] != "}":
98
+ return True
99
+
100
+ ##### otherwise, we don't add a space #####
101
+ return False
102
+
103
+
104
+ def _postprocess_reconstruct(items):
105
+ """
106
+ Postprocess the stream of tokens derived from the AST during reconstruction.
107
+
108
+ For HCL2, this is used exclusively for adding whitespace in the right locations.
109
+ """
110
+ prev_item = ""
111
+ for item in items:
112
+ # first, handle any deferred tokens
113
+ if isinstance(prev_item, tuple) and prev_item[0] == "_deferred":
114
+ prev_item = prev_item[1]
115
+
116
+ # if the deferred token was a comma, see if we're ending a block
117
+ if prev_item == ",":
118
+ if item[0] not in NEVER_COMMA_BEFORE:
119
+ yield prev_item
120
+ else:
121
+ yield prev_item
122
+
123
+ # if we're between two tokens, determine if we need to add an extra space
124
+ # we need the previous item and the current item to exist to evaluate these rules
125
+ if prev_item and item and _add_extra_space(prev_item, item):
126
+ yield " "
127
+
128
+ # in some cases, we may want to defer printing the next token
129
+ defer_item = False
130
+
131
+ # prevent the inclusion of extra commas if they are not intended
132
+ if item[0] == ",":
133
+ item = ("_deferred", item)
134
+ defer_item = True
135
+
136
+ # print the actual token
137
+ if not defer_item:
138
+ yield item
139
+
140
+ # store the previous item for the next token
141
+ prev_item = item
142
+
143
+ # if the last token was deferred, print it before continuing
144
+ if isinstance(prev_item, tuple) and prev_item[0] == "_deferred":
145
+ yield prev_item[1]
146
+
147
+
148
+ class HCLReconstructor:
149
+ """This class converts a Lark.Tree AST back into a string representing the underlying HCL code."""
150
+ def __init__(self, parser):
151
+ self._recons = Reconstructor(parser)
152
+
153
+ def reconstruct(self, tree):
154
+ """Convert a Lark.Tree AST back into a string representation of HCL."""
155
+ return self._recons.reconstruct(
156
+ tree,
157
+ _postprocess_reconstruct,
158
+ insert_spaces=False,
159
+ )
160
+
161
+
162
+ hcl2_reconstructor = HCLReconstructor(hcl2)
@@ -93,7 +93,10 @@ class DictTransformer(Transformer):
93
93
  # This returns a dict with a single key/value pair to make it easier to merge these
94
94
  # into a bigger dict that is returned by the "object" function
95
95
  key = self.strip_quotes(args[0])
96
- value = self.to_string_dollar(args[1])
96
+ if len(args) == 3:
97
+ value = self.to_string_dollar(args[2])
98
+ else:
99
+ value = self.to_string_dollar(args[1])
97
100
 
98
101
  return {key: value}
99
102
 
@@ -148,7 +151,7 @@ class DictTransformer(Transformer):
148
151
  key = str(args[0])
149
152
  if key.startswith('"') and key.endswith('"'):
150
153
  key = key[1:-1]
151
- value = self.to_string_dollar(args[1])
154
+ value = self.to_string_dollar(args[2])
152
155
  return Attribute(key, value)
153
156
 
154
157
  def conditional(self, args: List) -> str:
@@ -1,4 +1,4 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
- __version__ = version = '4.3.5'
4
- __version_tuple__ = version_tuple = (4, 3, 5)
3
+ __version__ = version = '5.0.0'
4
+ __version_tuple__ = version_tuple = (5, 0, 0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-hcl2
3
- Version: 4.3.5
3
+ Version: 5.0.0
4
4
  Summary: A parser for HCL2
5
5
  Author-email: Amplify Education <github@amplify.com>
6
6
  License: MIT
@@ -24,6 +24,7 @@ hcl2/api.py
24
24
  hcl2/hcl2.lark
25
25
  hcl2/parser.py
26
26
  hcl2/py.typed
27
+ hcl2/reconstructor.py
27
28
  hcl2/transformer.py
28
29
  hcl2/version.py
29
30
  python_hcl2.egg-info/PKG-INFO
@@ -1,28 +0,0 @@
1
- """The API that will be exposed to users of this package"""
2
- from typing import TextIO
3
-
4
- from hcl2.parser import hcl2
5
- from hcl2.transformer import DictTransformer
6
-
7
-
8
- def load(file: TextIO, with_meta=False) -> dict:
9
- """Load a HCL2 file.
10
- :param file: File with hcl2 to be loaded as a dict.
11
- :param with_meta: If set to true then adds `__start_line__` and `__end_line__`
12
- parameters to the output dict. Default to false.
13
- """
14
- return loads(file.read(), with_meta=with_meta)
15
-
16
-
17
- def loads(text: str, with_meta=False) -> dict:
18
- """Load HCL2 from a string.
19
- :param text: Text with hcl2 to be loaded as a dict.
20
- :param with_meta: If set to true then adds `__start_line__` and `__end_line__`
21
- parameters to the output dict. Default to false.
22
- """
23
- # append new line as a workaround for https://github.com/lark-parser/lark/issues/237
24
- # Lark doesn't support a EOF token so our grammar can't look for "new line or end of file"
25
- # This means that all blocks must end in a new line even if the file ends
26
- # Append a new line as a temporary fix
27
- tree = hcl2.parse(text + "\n")
28
- return DictTransformer(with_meta=with_meta).transform(tree)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes