|
1 | | -"""The API that will be exposed to users of this package""" |
2 | | -from typing import TextIO |
| 1 | +"""The API that will be exposed to users of this package. |
| 2 | +
|
| 3 | +Follows the json module convention: load/loads for reading, dump/dumps for writing. |
| 4 | +Also exposes intermediate pipeline stages for advanced usage. |
| 5 | +""" |
| 6 | + |
| 7 | +import json as _json |
| 8 | +from typing import TextIO, Optional |
3 | 9 |
|
4 | 10 | from lark.tree import Tree |
5 | | -from hcl2.parser import parser |
| 11 | + |
| 12 | +from hcl2.deserializer import BaseDeserializer, DeserializerOptions |
| 13 | +from hcl2.formatter import BaseFormatter, FormatterOptions |
| 14 | +from hcl2.parser import parser as _get_parser |
6 | 15 | from hcl2.reconstructor import HCLReconstructor |
| 16 | +from hcl2.rules.base import StartRule |
7 | 17 | from hcl2.transformer import RuleTransformer |
| 18 | +from hcl2.utils import SerializationOptions |
| 19 | + |
| 20 | + |
| 21 | +# --------------------------------------------------------------------------- |
| 22 | +# Primary API: load / loads / dump / dumps |
| 23 | +# --------------------------------------------------------------------------- |
| 24 | + |
| 25 | + |
| 26 | +def load( |
| 27 | + file: TextIO, |
| 28 | + *, |
| 29 | + serialization_options: Optional[SerializationOptions] = None, |
| 30 | +) -> dict: |
| 31 | + """Load a HCL2 file and return a Python dict. |
| 32 | +
|
| 33 | + :param file: File with HCL2 content. |
| 34 | + :param serialization_options: Options controlling serialization behavior. |
| 35 | + """ |
| 36 | + return loads(file.read(), serialization_options=serialization_options) |
| 37 | + |
| 38 | + |
| 39 | +def loads( |
| 40 | + text: str, |
| 41 | + *, |
| 42 | + serialization_options: Optional[SerializationOptions] = None, |
| 43 | +) -> dict: |
| 44 | + """Load HCL2 from a string and return a Python dict. |
8 | 45 |
|
| 46 | + :param text: HCL2 text. |
| 47 | + :param serialization_options: Options controlling serialization behavior. |
| 48 | + """ |
| 49 | + tree = parses(text) |
| 50 | + return serialize(tree, serialization_options=serialization_options) |
| 51 | + |
| 52 | + |
| 53 | +def dump( |
| 54 | + data: dict, |
| 55 | + file: TextIO, |
| 56 | + *, |
| 57 | + deserializer_options: Optional[DeserializerOptions] = None, |
| 58 | + formatter_options: Optional[FormatterOptions] = None, |
| 59 | +) -> None: |
| 60 | + """Write a Python dict as HCL2 to a file. |
9 | 61 |
|
10 | | -def load(file: TextIO, with_meta=False) -> dict: |
11 | | - """Load a HCL2 file. |
12 | | - :param file: File with hcl2 to be loaded as a dict. |
13 | | - :param with_meta: If set to true then adds `__start_line__` and `__end_line__` |
14 | | - parameters to the output dict. Default to false. |
| 62 | + :param data: Python dict (as produced by :func:`load`). |
| 63 | + :param file: Writable text file. |
| 64 | + :param deserializer_options: Options controlling deserialization behavior. |
| 65 | + :param formatter_options: Options controlling formatting behavior. |
15 | 66 | """ |
16 | | - return loads(file.read(), with_meta=with_meta) |
| 67 | + file.write(dumps(data, deserializer_options=deserializer_options, formatter_options=formatter_options)) |
17 | 68 |
|
18 | 69 |
|
19 | | -def loads(text: str, with_meta=False) -> dict: |
20 | | - """Load HCL2 from a string. |
21 | | - :param text: Text with hcl2 to be loaded as a dict. |
22 | | - :param with_meta: If set to true then adds `__start_line__` and `__end_line__` |
23 | | - parameters to the output dict. Default to false. |
| 70 | +def dumps( |
| 71 | + data: dict, |
| 72 | + *, |
| 73 | + deserializer_options: Optional[DeserializerOptions] = None, |
| 74 | + formatter_options: Optional[FormatterOptions] = None, |
| 75 | +) -> str: |
| 76 | + """Convert a Python dict to an HCL2 string. |
| 77 | +
|
| 78 | + :param data: Python dict (as produced by :func:`load`). |
| 79 | + :param deserializer_options: Options controlling deserialization behavior. |
| 80 | + :param formatter_options: Options controlling formatting behavior. |
24 | 81 | """ |
25 | | - # append new line as a workaround for https://github.com/lark-parser/lark/issues/237 |
| 82 | + tree = from_dict(data, deserializer_options=deserializer_options, formatter_options=formatter_options) |
| 83 | + return reconstruct(tree) |
| 84 | + |
| 85 | + |
| 86 | +# --------------------------------------------------------------------------- |
| 87 | +# Parsing: HCL text -> LarkElement tree or raw Lark tree |
| 88 | +# --------------------------------------------------------------------------- |
| 89 | + |
| 90 | + |
| 91 | +def parse(file: TextIO, *, discard_comments: bool = False) -> StartRule: |
| 92 | + """Parse a HCL2 file into a LarkElement tree. |
| 93 | +
|
| 94 | + :param file: File with HCL2 content. |
| 95 | + :param discard_comments: If True, discard comments during transformation. |
| 96 | + """ |
| 97 | + return parses(file.read(), discard_comments=discard_comments) |
| 98 | + |
| 99 | + |
| 100 | +def parses(text: str, *, discard_comments: bool = False) -> StartRule: |
| 101 | + """Parse a HCL2 string into a LarkElement tree. |
| 102 | +
|
| 103 | + :param text: HCL2 text. |
| 104 | + :param discard_comments: If True, discard comments during transformation. |
| 105 | + """ |
| 106 | + lark_tree = parses_to_tree(text) |
| 107 | + return transform(lark_tree, discard_comments=discard_comments) |
| 108 | + |
| 109 | + |
| 110 | +def parse_to_tree(file: TextIO) -> Tree: |
| 111 | + """Parse a HCL2 file into a raw Lark parse tree. |
| 112 | +
|
| 113 | + :param file: File with HCL2 content. |
| 114 | + """ |
| 115 | + return parses_to_tree(file.read()) |
| 116 | + |
| 117 | + |
| 118 | +def parses_to_tree(text: str) -> Tree: |
| 119 | + """Parse a HCL2 string into a raw Lark parse tree. |
| 120 | +
|
| 121 | + :param text: HCL2 text. |
| 122 | + """ |
| 123 | + # Append newline as workaround for https://github.com/lark-parser/lark/issues/237 |
26 | 124 | # Lark doesn't support EOF token so our grammar can't look for "new line or end of file" |
27 | | - # This means that all blocks must end in a new line even if the file ends |
28 | | - # Append a new line as a temporary fix |
29 | | - tree = parser().parse(text + "\n") |
30 | | - return RuleTransformer().transform(tree) |
| 125 | + return _get_parser().parse(text + "\n") |
31 | 126 |
|
32 | 127 |
|
33 | | -def parse(file: TextIO) -> Tree: |
34 | | - """Load HCL2 syntax tree from a file. |
35 | | - :param file: File with hcl2 to be loaded as a dict. |
| 128 | +# --------------------------------------------------------------------------- |
| 129 | +# Intermediate pipeline stages |
| 130 | +# --------------------------------------------------------------------------- |
| 131 | + |
| 132 | + |
| 133 | +def from_dict( |
| 134 | + data: dict, |
| 135 | + *, |
| 136 | + deserializer_options: Optional[DeserializerOptions] = None, |
| 137 | + formatter_options: Optional[FormatterOptions] = None, |
| 138 | + format: bool = True, |
| 139 | +) -> StartRule: |
| 140 | + """Convert a Python dict into a LarkElement tree. |
| 141 | +
|
| 142 | + :param data: Python dict (as produced by :func:`load`). |
| 143 | + :param deserializer_options: Options controlling deserialization behavior. |
| 144 | + :param formatter_options: Options controlling formatting behavior. |
| 145 | + :param format: If True (default), apply formatting to the tree. |
| 146 | + """ |
| 147 | + deserializer = BaseDeserializer(deserializer_options) |
| 148 | + tree = deserializer.load_python(data) |
| 149 | + if format: |
| 150 | + formatter = BaseFormatter(formatter_options) |
| 151 | + formatter.format_tree(tree) |
| 152 | + return tree |
| 153 | + |
| 154 | + |
| 155 | +def from_json( |
| 156 | + text: str, |
| 157 | + *, |
| 158 | + deserializer_options: Optional[DeserializerOptions] = None, |
| 159 | + formatter_options: Optional[FormatterOptions] = None, |
| 160 | + format: bool = True, |
| 161 | +) -> StartRule: |
| 162 | + """Convert a JSON string into a LarkElement tree. |
| 163 | +
|
| 164 | + :param text: JSON string. |
| 165 | + :param deserializer_options: Options controlling deserialization behavior. |
| 166 | + :param formatter_options: Options controlling formatting behavior. |
| 167 | + :param format: If True (default), apply formatting to the tree. |
36 | 168 | """ |
37 | | - return parses(file.read()) |
| 169 | + data = _json.loads(text) |
| 170 | + return from_dict(data, deserializer_options=deserializer_options, formatter_options=formatter_options, format=format) |
| 171 | + |
38 | 172 |
|
| 173 | +def reconstruct(tree) -> str: |
| 174 | + """Convert a LarkElement tree (or raw Lark tree) to an HCL2 string. |
39 | 175 |
|
40 | | -def parses(text: str) -> Tree: |
41 | | - """Load HCL2 syntax tree from a string. |
42 | | - :param text: Text with hcl2 to be loaded as a dict. |
| 176 | + :param tree: A :class:`StartRule` (LarkElement tree) or :class:`lark.Tree`. |
43 | 177 | """ |
44 | | - return parser().parse(text) |
| 178 | + reconstructor = HCLReconstructor() |
| 179 | + if isinstance(tree, StartRule): |
| 180 | + tree = tree.to_lark() |
| 181 | + return reconstructor.reconstruct(tree) |
45 | 182 |
|
46 | 183 |
|
47 | | -def transform(ast: Tree, with_meta=False) -> dict: |
48 | | - """Convert an HCL2 AST to a dictionary. |
49 | | - :param ast: HCL2 syntax tree, output from `parse` or `parses` |
50 | | - :param with_meta: If set to true then adds `__start_line__` and `__end_line__` |
51 | | - parameters to the output dict. Default to false. |
| 184 | +def transform(lark_tree: Tree, *, discard_comments: bool = False) -> StartRule: |
| 185 | + """Transform a raw Lark parse tree into a LarkElement tree. |
| 186 | +
|
| 187 | + :param lark_tree: Raw Lark tree from :func:`parse_to_tree` or :func:`parse_string_to_tree`. |
| 188 | + :param discard_comments: If True, discard comments during transformation. |
52 | 189 | """ |
53 | | - return RuleTransformer().transform(ast) |
| 190 | + return RuleTransformer(discard_new_line_or_comments=discard_comments).transform(lark_tree) |
| 191 | + |
54 | 192 |
|
| 193 | +def serialize( |
| 194 | + tree: StartRule, |
| 195 | + *, |
| 196 | + serialization_options: Optional[SerializationOptions] = None, |
| 197 | +) -> dict: |
| 198 | + """Serialize a LarkElement tree to a Python dict. |
55 | 199 |
|
56 | | -def writes(ast: Tree) -> str: |
57 | | - """Convert an HCL2 syntax tree to a string. |
58 | | - :param ast: HCL2 syntax tree, output from `parse` or `parses` |
| 200 | + :param tree: A :class:`StartRule` (LarkElement tree). |
| 201 | + :param serialization_options: Options controlling serialization behavior. |
59 | 202 | """ |
60 | | - return HCLReconstructor().reconstruct(ast) |
| 203 | + if serialization_options is not None: |
| 204 | + return tree.serialize(options=serialization_options) |
| 205 | + return tree.serialize() |
0 commit comments