Skip to content

Commit 6c8c9aa

Browse files
Add v7-to-v8 migration guide and use absolute GitHub links in README docs table (#286) (#287)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7fe9291 commit 6c8c9aa

3 files changed

Lines changed: 214 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77

88
## \[Unreleased\]
99

10-
- Nothing yet.
10+
### Added
11+
12+
- v7-to-v8 migration guide and absolute GitHub links in README docs table. ([#287](https://github.com/amplify-education/python-hcl2/pull/287))
1113

1214
## \[8.1.0\] - 2026-04-07
1315

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ hcl_string = hcl2.dumps(doc.build())
7878

7979
| Guide | Contents |
8080
|---|---|
81-
| [Getting Started](docs/01_getting_started.md) | Installation, load/dump, options, CLI converters |
82-
| [Querying HCL (Python)](docs/02_querying.md) | DocumentView, BlockView, tree walking, view hierarchy |
83-
| [Advanced API](docs/03_advanced_api.md) | Pipeline stages, Builder |
84-
| [hq Reference](docs/04_hq.md) | `hq` CLI — structural queries, hybrid/eval, introspection |
85-
| [hq Examples](docs/05_hq_examples.md) | Real-world queries for discovery, compliance, extraction |
81+
| [Getting Started](https://github.com/amplify-education/python-hcl2/blob/main/docs/01_getting_started.md) | Installation, load/dump, options, CLI converters |
82+
| [Querying HCL (Python)](https://github.com/amplify-education/python-hcl2/blob/main/docs/02_querying.md) | DocumentView, BlockView, tree walking, view hierarchy |
83+
| [Advanced API](https://github.com/amplify-education/python-hcl2/blob/main/docs/03_advanced_api.md) | Pipeline stages, Builder |
84+
| [hq Reference](https://github.com/amplify-education/python-hcl2/blob/main/docs/04_hq.md) | `hq` CLI — structural queries, hybrid/eval, introspection |
85+
| [hq Examples](https://github.com/amplify-education/python-hcl2/blob/main/docs/05_hq_examples.md) | Real-world queries for discovery, compliance, extraction |
86+
| [Migrating to v8](https://github.com/amplify-education/python-hcl2/blob/main/docs/06_migrating_to_v8.md) | Breaking changes, updated API patterns, v7-compat options |
8687

8788
### CLI Tools
8889

docs/06_migrating_to_v8.md

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Migrating to v8
2+
3+
This guide covers breaking changes when upgrading from python-hcl2 v7 to v8. Changes are ordered by likelihood of impact — if you only use `load()`/`loads()` to read HCL files, focus on the first three sections.
4+
5+
## String values now include HCL quotes
6+
7+
**Impact: high** — silently changes output without raising errors.
8+
9+
In v7, `load()` stripped the surrounding double-quotes from HCL string values. In v8, quotes are preserved by default to enable lossless round-trips.
10+
11+
```python
12+
# Given: name = "hello"
13+
14+
# v7
15+
data["name"] # 'hello'
16+
17+
# v8 (default)
18+
data["name"] # '"hello"'
19+
```
20+
21+
To restore v7 behavior:
22+
23+
```python
24+
import hcl2
25+
from hcl2 import SerializationOptions
26+
27+
data = hcl2.load(f, serialization_options=SerializationOptions(strip_string_quotes=True))
28+
```
29+
30+
> **Note:** `strip_string_quotes=True` is one-way — dicts produced with it cannot round-trip back to HCL via `dumps()` because the quotes needed to distinguish strings from identifiers are gone.
31+
32+
## New metadata keys in output dicts
33+
34+
**Impact: high** — code that iterates keys or does exact-match assertions will break.
35+
36+
v8 adds two new key categories to output dicts by default:
37+
38+
| Key | Default | Purpose |
39+
|---|---|---|
40+
| `__is_block__` | on (`explicit_blocks=True`) | Distinguishes HCL blocks from plain objects |
41+
| `__comments__`, `__inline_comments__` | on (`with_comments=True`) | Preserves HCL comments |
42+
43+
To suppress them:
44+
45+
```python
46+
opts = SerializationOptions(explicit_blocks=False, with_comments=False)
47+
data = hcl2.load(f, serialization_options=opts)
48+
```
49+
50+
> **Note:** `explicit_blocks=False` disables round-trip support via `dumps()` — the deserializer needs `__is_block__` markers to reconstruct blocks correctly.
51+
52+
The v7 metadata keys `__start_line__` and `__end_line__` are still available but remain opt-in:
53+
54+
```python
55+
opts = SerializationOptions(with_meta=True)
56+
```
57+
58+
## `load()` / `loads()` signature changed
59+
60+
**Impact: high** — calls using `with_meta` will raise `TypeError`.
61+
62+
The `with_meta` positional/keyword parameter has been replaced by a `SerializationOptions` object:
63+
64+
```python
65+
# v7
66+
data = hcl2.load(f, with_meta=True)
67+
data = hcl2.loads(text, with_meta=True)
68+
69+
# v8
70+
from hcl2 import SerializationOptions
71+
data = hcl2.load(f, serialization_options=SerializationOptions(with_meta=True))
72+
data = hcl2.loads(text, serialization_options=SerializationOptions(with_meta=True))
73+
```
74+
75+
All parameters on `load()`/`loads()` are now keyword-only.
76+
77+
## `reverse_transform()` and `writes()` removed
78+
79+
**Impact: medium** — calls will raise `ImportError` / `AttributeError`.
80+
81+
The v7 two-step dict-to-HCL workflow has been replaced by `dump()`/`dumps()`:
82+
83+
```python
84+
# v7
85+
ast = hcl2.reverse_transform(data)
86+
text = hcl2.writes(ast)
87+
88+
# v8
89+
text = hcl2.dumps(data)
90+
91+
# or to a file:
92+
with open("output.tf", "w") as f:
93+
hcl2.dump(data, f)
94+
```
95+
96+
`dumps()` accepts optional `deserializer_options` and `formatter_options` for controlling the output:
97+
98+
```python
99+
from hcl2 import DeserializerOptions, FormatterOptions
100+
101+
text = hcl2.dumps(
102+
data,
103+
deserializer_options=DeserializerOptions(object_elements_colon=True),
104+
formatter_options=FormatterOptions(indent_length=4),
105+
)
106+
```
107+
108+
## `parse()` / `parses()` return type changed
109+
110+
**Impact: medium** — code accessing Lark tree internals will break.
111+
112+
These functions now return a typed `StartRule` (a `LarkElement` node) instead of a raw `lark.Tree`:
113+
114+
```python
115+
# v7
116+
tree = hcl2.parses(text) # -> lark.Tree
117+
tree.data # 'start'
118+
tree.children # [lark.Tree, ...]
119+
120+
# v8
121+
tree = hcl2.parses(text) # -> StartRule
122+
tree.body # typed BodyRule accessor
123+
```
124+
125+
If you need the raw Lark tree, use the new explicit functions:
126+
127+
```python
128+
lark_tree = hcl2.parses_to_tree(text) # -> lark.Tree (raw)
129+
rule_tree = hcl2.transform(lark_tree) # -> StartRule (typed)
130+
```
131+
132+
## `transform()` signature and return type changed
133+
134+
**Impact: medium** — same cause as above.
135+
136+
```python
137+
# v7
138+
data = hcl2.transform(ast, with_meta=True) # -> dict
139+
140+
# v8
141+
rule_tree = hcl2.transform(lark_tree, discard_comments=False) # -> StartRule
142+
data = hcl2.serialize(rule_tree, serialization_options=opts) # -> dict
143+
```
144+
145+
In v8, `transform()` produces a typed IR tree. To get a dict, follow it with `serialize()`.
146+
147+
## `DictTransformer` and `reconstruction_parser` removed
148+
149+
**Impact: low** — only affects code importing internals.
150+
151+
| v7 import | v8 replacement |
152+
|---|---|
153+
| `from hcl2.transformer import DictTransformer` | Use `hcl2.transform()` + `hcl2.serialize()` |
154+
| `from hcl2.parser import reconstruction_parser` | Use `hcl2.parser.parser()` (single parser) |
155+
| `from hcl2.reconstructor import HCLReverseTransformer` | Use `hcl2.from_dict()` + `hcl2.reconstruct()` |
156+
157+
## New pipeline stages
158+
159+
v8 exposes the full bidirectional pipeline as composable functions:
160+
161+
```
162+
Forward: HCL text -> parses_to_tree() -> transform() -> serialize() -> dict
163+
Reverse: dict -> from_dict() -> reconstruct() -> HCL text
164+
```
165+
166+
| Function | Input | Output |
167+
|---|---|---|
168+
| `parses_to_tree(text)` | HCL string | raw `lark.Tree` |
169+
| `transform(lark_tree)` | `lark.Tree` | `StartRule` |
170+
| `serialize(tree)` | `StartRule` | `dict` |
171+
| `from_dict(data)` | `dict` | `StartRule` |
172+
| `from_json(text)` | JSON string | `StartRule` |
173+
| `reconstruct(tree)` | `StartRule` | HCL string |
174+
175+
## CLI changes
176+
177+
The `hcl2tojson` entry point moved from `hcl2.__main__:main` to `cli.hcl_to_json:main`. A shim keeps `python -m hcl2` working, but direct imports from `hcl2.__main__` should be updated.
178+
179+
Two new CLI tools ship with v8:
180+
181+
- **`jsontohcl2`** — convert JSON back to HCL2, with diff/dry-run support
182+
- **`hq`** — structural query tool for HCL files (jq-like syntax)
183+
184+
## Python 3.7 no longer supported
185+
186+
The minimum Python version is now **3.8**.
187+
188+
## Quick reference: v7-compatible defaults
189+
190+
If you want v8 to behave as closely to v7 as possible:
191+
192+
```python
193+
import hcl2
194+
from hcl2 import SerializationOptions
195+
196+
V7_COMPAT = SerializationOptions(
197+
strip_string_quotes=True,
198+
explicit_blocks=False,
199+
with_comments=False,
200+
)
201+
202+
data = hcl2.load(f, serialization_options=V7_COMPAT)
203+
```
204+
205+
This restores the v7 dict shape but disables round-trip support and comment preservation.

0 commit comments

Comments
 (0)