-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support full reconstruction of HCL from output dictionary (#177)
* Initial commit of a "reverse transformer" to turn HCL2 dicts into Lark trees * Add tests for the reverse reconstructor * Add different handling to the reverse reconstructor depending on data type * Add support for multiple block labels * Fix accidentally escaping quotes within interpolated strings * Properly handle escapes within HCL strings (closes #171) * Standardize string output from transformer within nested structures to match Terraform syntax instead of Python (fixes #172) * Fix block labels and booleans during reconstruction * Better handle nested interpolation (fixes #173) * Begin refactor of whitespace handling (more to come) * overhaul of whitespace handling, remove old logic. * Fix Pylint warnings * Fix a few formatting issues in reconstruction * Add a "builder" class for constructing HCL files from Python * Update the docs for reconstruction * fix suggested by Nfsaavedra #177 (comment) * a bit of refactoring * update interpolation test case to include long non-interpolated substring --------- Co-authored-by: Kamil Kozik <[email protected]>
- Loading branch information
1 parent
eb2032a
commit f8a2c88
Showing
26 changed files
with
1,215 additions
and
190 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
"""A utility class for constructing HCL documents from Python code.""" | ||
|
||
from typing import List, Optional | ||
|
||
|
||
class Builder: | ||
""" | ||
The `hcl2.Builder` class produces a dictionary that should be identical to the | ||
output of `hcl2.load(example_file, with_meta=True)`. The `with_meta` keyword | ||
argument is important here. HCL "blocks" in the Python dictionary are | ||
identified by the presence of `__start_line__` and `__end_line__` metadata | ||
within them. The `Builder` class handles adding that metadata. If that metadata | ||
is missing, the `hcl2.reconstructor.HCLReverseTransformer` class fails to | ||
identify what is a block and what is just an attribute with an object value. | ||
""" | ||
|
||
def __init__(self, attributes: Optional[dict] = None): | ||
self.blocks: dict = {} | ||
self.attributes = attributes or {} | ||
|
||
def block( | ||
self, block_type: str, labels: Optional[List[str]] = None, **attributes | ||
) -> "Builder": | ||
"""Create a block within this HCL document.""" | ||
labels = labels or [] | ||
block = Builder(attributes) | ||
|
||
# initialize a holder for blocks of that type | ||
if block_type not in self.blocks: | ||
self.blocks[block_type] = [] | ||
|
||
# store the block in the document | ||
self.blocks[block_type].append((labels.copy(), block)) | ||
|
||
return block | ||
|
||
def build(self): | ||
"""Return the Python dictionary for this HCL document.""" | ||
body = { | ||
"__start_line__": -1, | ||
"__end_line__": -1, | ||
**self.attributes, | ||
} | ||
|
||
for block_type, blocks in self.blocks.items(): | ||
|
||
# initialize a holder for blocks of that type | ||
if block_type not in body: | ||
body[block_type] = [] | ||
|
||
for labels, block_builder in blocks: | ||
# build the sub-block | ||
block = block_builder.build() | ||
|
||
# apply any labels | ||
labels.reverse() | ||
for label in labels: | ||
block = {label: block} | ||
|
||
# store it in the body | ||
body[block_type].append(block) | ||
|
||
return body |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.