yamlsmith 0.1.1__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.
@@ -0,0 +1,10 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ - package-ecosystem: "github-actions"
8
+ directory: "/"
9
+ schedule:
10
+ interval: "weekly"
@@ -0,0 +1,41 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ matrix:
14
+ os: [ubuntu-latest, macos-latest]
15
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
16
+ fail-fast: false
17
+ steps:
18
+ - uses: actions/checkout@v6
19
+ - uses: actions/setup-python@v6
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ - name: Install uv
23
+ uses: astral-sh/setup-uv@v7
24
+ - name: Install dependencies
25
+ run: uv sync
26
+ - name: Run tests
27
+ run: uv run pytest tests/ -v
28
+
29
+ typecheck:
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v6
33
+ - uses: actions/setup-python@v6
34
+ with:
35
+ python-version: "3.13"
36
+ - name: Install uv
37
+ uses: astral-sh/setup-uv@v7
38
+ - name: Install dependencies
39
+ run: uv sync
40
+ - name: Run mypy
41
+ run: uv run mypy src/ --strict
@@ -0,0 +1,20 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v7
17
+ - name: Build distribution
18
+ run: uv build
19
+ - name: Publish to PyPI
20
+ run: uv publish
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ .venv/
5
+ .env
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+ .mypy_cache/
10
+ .pytest_cache/
11
+ .ruff_cache/
12
+ coverage/
13
+ htmlcov/
14
+ .coverage
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ All notable changes to yamlsmith are documented here.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## v0.1.1 (2026-03-15)
8
+
9
+ ### Fixed
10
+
11
+ - **Pre-comment placement:** Pre-comments (standalone comment lines before a key) were incorrectly emitted as inline comments on the previous key line. They are now preserved on their own line above the key.
12
+ - **Integer/float/bool round-trip quoting:** Plain scalars such as `5432` were quoted after a load→dump cycle. The constructor now stores `None` as the tag for implicitly resolved non-string types so the representer derives the correct tag from the Python type rather than a stale string tag stored in `RoundTripScalar`.
13
+ - **Plain scalar quoting:** Unquoted scalars that resolve to non-string types no longer receive spurious quotes on re-emit.
14
+
15
+ ## v0.1.0 (2026-03-14)
16
+
17
+ Initial release.
18
+
19
+ ### Added
20
+
21
+ - YAML 1.2 scanner/tokenizer with comment metadata
22
+ - Event-stream parser
23
+ - Node graph composer with anchor/alias resolution
24
+ - Constructor: YAML nodes → Python objects (`str`, `int`, `float`, `bool`, `None`, `datetime`, `bytes`)
25
+ - Representer: Python objects → YAML nodes
26
+ - Emitter with comment preservation and block/flow style support
27
+ - Round-trip types: `RoundTripDict`, `RoundTripList`, `RoundTripScalar`
28
+ - Public API: `YAML` class and `load` / `dump` / `load_all` / `dump_all` convenience functions
29
+ - Error hierarchy: `YAMLError`, `ScannerError`, `ParserError`, `ComposerError`, `ConstructorError`, `EmitterError`
30
+ - PEP 561 typed package (`py.typed` marker)
31
+ - Python 3.10+ support
32
+ - Zero runtime dependencies
@@ -0,0 +1,22 @@
1
+ .PHONY: install build test lint fmt clean ci
2
+
3
+ install:
4
+ uv sync
5
+
6
+ build:
7
+ uv build
8
+
9
+ test:
10
+ uv run pytest tests/ -v
11
+
12
+ lint:
13
+ uv run ruff check src/
14
+ uv run mypy src/
15
+
16
+ fmt:
17
+ uv run ruff format src/ tests/
18
+
19
+ clean:
20
+ rm -rf dist/ build/ *.egg-info/ .pytest_cache/ .mypy_cache/
21
+
22
+ ci: lint test
@@ -0,0 +1,377 @@
1
+ Metadata-Version: 2.4
2
+ Name: yamlsmith
3
+ Version: 0.1.1
4
+ Summary: Round-trip YAML 1.2 library with comment preservation — a modern ruamel.yaml replacement
5
+ Project-URL: Homepage, https://github.com/agentine/yamlsmith
6
+ Project-URL: Repository, https://github.com/agentine/yamlsmith
7
+ Project-URL: Issue Tracker, https://github.com/agentine/yamlsmith/issues
8
+ Author: Agentine
9
+ License-Expression: MIT
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+
22
+ # yamlsmith
23
+
24
+ [![PyPI](https://img.shields.io/pypi/v/yamlsmith)](https://pypi.org/project/yamlsmith/)
25
+ [![Python](https://img.shields.io/pypi/pyversions/yamlsmith)](https://pypi.org/project/yamlsmith/)
26
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
27
+ [![Typed](https://img.shields.io/badge/typing-strict-green)](https://mypy-lang.org/)
28
+
29
+ Round-trip YAML 1.2 library with comment preservation — a modern, drop-in replacement for ruamel.yaml.
30
+
31
+ Pure Python, zero dependencies, fully typed ([PEP 561](https://peps.python.org/pep-0561/)), Python 3.10+.
32
+
33
+ ---
34
+
35
+ ## Why yamlsmith?
36
+
37
+ [ruamel.yaml](https://pypi.org/project/ruamel.yaml/) has 155M+ monthly downloads and is the only production-ready Python library for round-trip YAML with comment preservation. But it carries significant risk:
38
+
39
+ - **Bus factor = 1.** Single maintainer with no governance structure.
40
+ - **PEP 625 crisis.** The namespace package naming may prevent continued PyPI uploads.
41
+ - **Hostile contribution model.** SourceForge/Mercurial hosting makes community contribution nearly impossible.
42
+ - **Failed fork.** ruyaml (pycontribs) was created to address these risks and itself stalled.
43
+
44
+ yamlsmith solves this: same round-trip semantics, modern codebase, zero dependencies, YAML 1.2 strict mode (no legacy boolean quirks from 1.1).
45
+
46
+ ---
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ pip install yamlsmith
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Quick Start
57
+
58
+ ### Load and modify without losing comments
59
+
60
+ ```python
61
+ from yamlsmith import load, dump
62
+
63
+ text = """\
64
+ # Database configuration
65
+ host: localhost
66
+ port: 5432 # default PostgreSQL port
67
+ enabled: true
68
+ """
69
+
70
+ data = load(text)
71
+ data["port"] = 5433 # modify a value
72
+ data["timeout"] = 30 # add a new key
73
+
74
+ print(dump(data))
75
+ ```
76
+
77
+ Output — comments preserved, structure intact:
78
+
79
+ ```yaml
80
+ # Database configuration
81
+ host: localhost
82
+ port: 5433 # default PostgreSQL port
83
+ enabled: true
84
+ timeout: 30
85
+ ```
86
+
87
+ ### Multi-document streams
88
+
89
+ ```python
90
+ from yamlsmith import load_all, dump_all
91
+
92
+ text = """\
93
+ # Document 1
94
+ name: alice
95
+ ---
96
+ # Document 2
97
+ name: bob
98
+ """
99
+
100
+ docs = load_all(text)
101
+ docs[0]["name"] = "ALICE"
102
+ print(dump_all(docs))
103
+ ```
104
+
105
+ ### File I/O
106
+
107
+ ```python
108
+ from yamlsmith import YAML
109
+
110
+ yaml = YAML()
111
+
112
+ with open("config.yaml") as f:
113
+ data = yaml.load(f)
114
+
115
+ data["version"] = "2.0"
116
+
117
+ with open("config.yaml", "w") as f:
118
+ yaml.dump(data, f)
119
+ ```
120
+
121
+ ---
122
+
123
+ ## API Reference
124
+
125
+ ### Convenience functions
126
+
127
+ ```python
128
+ from yamlsmith import load, dump, load_all, dump_all
129
+ ```
130
+
131
+ | Function | Signature | Description |
132
+ |---|---|---|
133
+ | `load` | `(text: str \| bytes \| IO) → Any` | Load a single YAML document |
134
+ | `dump` | `(data: Any, stream: IO \| None = None) → str` | Dump to YAML string (optionally also write to stream) |
135
+ | `load_all` | `(text: str \| bytes \| IO) → list[Any]` | Load all documents from a multi-document stream |
136
+ | `dump_all` | `(data: list[Any], stream: IO \| None = None) → str` | Dump a list of documents separated by `---` |
137
+
138
+ All functions use round-trip mode: mappings and sequences are loaded as `RoundTripDict` / `RoundTripList` with comment metadata attached.
139
+
140
+ ---
141
+
142
+ ### `YAML` class
143
+
144
+ ```python
145
+ from yamlsmith import YAML
146
+
147
+ yaml = YAML(indent=2, default_flow_style=False)
148
+ ```
149
+
150
+ **Constructor parameters:**
151
+
152
+ | Parameter | Type | Default | Description |
153
+ |---|---|---|---|
154
+ | `indent` | `int` | `2` | Indentation width for block mappings and sequences |
155
+ | `default_flow_style` | `bool` | `False` | Emit flow style (`{a: 1}`) instead of block style by default |
156
+
157
+ **Methods:**
158
+
159
+ | Method | Signature | Description |
160
+ |---|---|---|
161
+ | `load` | `(stream: str \| bytes \| IO) → Any` | Load a single YAML document |
162
+ | `dump` | `(data: Any, stream: IO \| None = None) → str` | Dump to YAML string, optionally also write to stream |
163
+ | `load_all` | `(stream: str \| bytes \| IO) → list[Any]` | Load all documents from a stream |
164
+ | `dump_all` | `(data: list[Any], stream: IO \| None = None) → str` | Dump multiple documents with `---` separators |
165
+
166
+ Streams may be `str`, `bytes`, or any file-like object with a `.read()` / `.write()` method.
167
+
168
+ ---
169
+
170
+ ### Round-trip types
171
+
172
+ When you load YAML, mappings and sequences are returned as round-trip types that carry comment metadata:
173
+
174
+ #### `RoundTripDict`
175
+
176
+ A `dict` subclass preserving insertion order and YAML comments.
177
+
178
+ ```python
179
+ from yamlsmith import RoundTripDict
180
+
181
+ d = RoundTripDict({"a": 1, "b": 2})
182
+
183
+ # Attach comments to a key
184
+ d.set_comment("a", pre="# section header", inline="# inline note")
185
+
186
+ # Read comments back
187
+ pre, inline = d.get_comment("a")
188
+ ```
189
+
190
+ | Method | Signature | Description |
191
+ |---|---|---|
192
+ | `set_comment` | `(key, *, pre=None, inline=None)` | Attach a pre-comment and/or inline comment to `key` |
193
+ | `get_comment` | `(key) → tuple[str \| None, str \| None]` | Return `(pre_comment, inline_comment)` for `key` |
194
+
195
+ #### `RoundTripList`
196
+
197
+ A `list` subclass preserving YAML comments per item.
198
+
199
+ ```python
200
+ from yamlsmith import RoundTripList
201
+
202
+ lst = RoundTripList([1, 2, 3])
203
+
204
+ # Attach a comment to item at index 0
205
+ lst.set_item_comment(0, pre="# first item", inline="# note")
206
+
207
+ pre, inline = lst.get_item_comment(0)
208
+ ```
209
+
210
+ | Method | Signature | Description |
211
+ |---|---|---|
212
+ | `set_item_comment` | `(index, *, pre=None, inline=None)` | Attach a pre-comment and/or inline comment to item `index` |
213
+ | `get_item_comment` | `(index) → tuple[str \| None, str \| None]` | Return `(pre_comment, inline_comment)` for item `index` |
214
+
215
+ #### `RoundTripScalar`
216
+
217
+ A wrapper for a scalar value that carries comment metadata and style information. Returned by the loader when a scalar has an inline comment or non-default quoting style.
218
+
219
+ ```python
220
+ from yamlsmith import RoundTripScalar
221
+
222
+ s = RoundTripScalar(42, inline_comment="# answer", style="plain")
223
+ print(s.value) # 42
224
+ print(s == 42) # True — compares by .value
225
+ ```
226
+
227
+ | Attribute | Type | Description |
228
+ |---|---|---|
229
+ | `value` | `Any` | The underlying Python value |
230
+ | `pre_comment` | `str \| None` | Comment line(s) before the scalar |
231
+ | `inline_comment` | `str \| None` | Comment after the value on the same line |
232
+ | `style` | `str \| None` | Scalar style: `plain`, `single`, `double`, `literal`, `folded` |
233
+ | `tag` | `str \| None` | Explicit YAML tag, or `None` for implicit resolution |
234
+
235
+ ---
236
+
237
+ ### Error types
238
+
239
+ All errors inherit from `YAMLError`.
240
+
241
+ ```python
242
+ from yamlsmith import YAMLError, ScannerError, ParserError, ComposerError, ConstructorError, EmitterError
243
+
244
+ try:
245
+ data = load("key: :")
246
+ except YAMLError as e:
247
+ print(f"YAML error: {e}")
248
+ ```
249
+
250
+ | Exception | Raised when |
251
+ |---|---|
252
+ | `YAMLError` | Base class for all yamlsmith errors |
253
+ | `ScannerError` | Invalid character or token in the input stream |
254
+ | `ParserError` | Structurally invalid YAML (bad nesting, missing values) |
255
+ | `ComposerError` | Undefined alias reference (`*anchor` without `&anchor`) |
256
+ | `ConstructorError` | Unknown YAML tag or type conversion failure |
257
+ | `EmitterError` | Serialization failure during emit |
258
+
259
+ ---
260
+
261
+ ## Comment Preservation
262
+
263
+ yamlsmith attaches comments to the nearest YAML node:
264
+
265
+ | Comment type | Example | Stored on |
266
+ |---|---|---|
267
+ | Pre-comment | `# header` on its own line before a key | the key's node |
268
+ | Inline comment | `value # note` after a value | the value's node |
269
+ | Post-comment | trailing comment after a block | the block node |
270
+ | Document comment | comment before `---` or after `...` | the document node |
271
+
272
+ Comments survive load → modify → dump cycles as long as the node they are attached to is not replaced with a plain Python object. If you replace a `RoundTripDict` with a plain `dict`, its comments are discarded.
273
+
274
+ ---
275
+
276
+ ## YAML 1.2 Strict Mode
277
+
278
+ yamlsmith implements **YAML 1.2 only**. Legacy YAML 1.1 boolean strings are treated as plain strings:
279
+
280
+ | Expression | ruamel.yaml (1.1) | yamlsmith (1.2) |
281
+ |---|---|---|
282
+ | `yes` | `True` | `"yes"` |
283
+ | `no` | `False` | `"no"` |
284
+ | `on` | `True` | `"on"` |
285
+ | `off` | `False` | `"off"` |
286
+ | `true` | `True` | `True` |
287
+ | `false` | `False` | `False` |
288
+
289
+ Only `true`/`True`/`TRUE` and `false`/`False`/`FALSE` are recognised as booleans.
290
+
291
+ ---
292
+
293
+ ## Migration from ruamel.yaml
294
+
295
+ ### Import changes
296
+
297
+ | ruamel.yaml | yamlsmith |
298
+ |---|---|
299
+ | `from ruamel.yaml import YAML` | `from yamlsmith import YAML` |
300
+ | `from ruamel.yaml.comments import CommentedMap` | `from yamlsmith import RoundTripDict` |
301
+ | `from ruamel.yaml.comments import CommentedSeq` | `from yamlsmith import RoundTripList` |
302
+
303
+ ### API compatibility
304
+
305
+ The core `YAML` class API is identical:
306
+
307
+ ```python
308
+ # ruamel.yaml
309
+ from ruamel.yaml import YAML
310
+ yaml = YAML()
311
+ data = yaml.load(stream)
312
+ yaml.dump(data, stream)
313
+
314
+ # yamlsmith — same calls
315
+ from yamlsmith import YAML
316
+ yaml = YAML()
317
+ data = yaml.load(stream)
318
+ yaml.dump(data, stream)
319
+ ```
320
+
321
+ ### Type name changes
322
+
323
+ | ruamel.yaml | yamlsmith | Notes |
324
+ |---|---|---|
325
+ | `CommentedMap` | `RoundTripDict` | Same dict semantics |
326
+ | `CommentedSeq` | `RoundTripList` | Same list semantics |
327
+ | `CommentedSeq` | `RoundTripList` | Same list semantics |
328
+ | `scalarstring.*` | `RoundTripScalar(style=...)` | Unified scalar wrapper |
329
+
330
+ ### Behaviour differences
331
+
332
+ | Behaviour | ruamel.yaml | yamlsmith |
333
+ |---|---|---|
334
+ | YAML spec | 1.1 + 1.2 hybrid | 1.2 strict |
335
+ | `yes`/`no`/`on`/`off` | booleans | plain strings |
336
+ | `dump()` return value | `None` (writes to stream) | always returns `str` |
337
+ | Initialisation | `YAML(typ="rt")` for round-trip | round-trip is the only mode |
338
+ | Python object tags | `!!python/object` supported | not supported (safe by default) |
339
+
340
+ ### dump() return value
341
+
342
+ ruamel.yaml's `dump()` writes to a stream and returns `None`. yamlsmith's `dump()` always returns the YAML string and optionally also writes to the stream if one is provided:
343
+
344
+ ```python
345
+ # ruamel.yaml
346
+ import io
347
+ buf = io.StringIO()
348
+ yaml.dump(data, buf)
349
+ text = buf.getvalue()
350
+
351
+ # yamlsmith — simpler
352
+ text = yaml.dump(data) # or:
353
+ text = yaml.dump(data, stream=f) # also writes to f
354
+ ```
355
+
356
+ ---
357
+
358
+ ## Features
359
+
360
+ - YAML 1.2 strict mode (no legacy 1.1 boolean quirks)
361
+ - Round-trip comment preservation (pre, inline, post, document-level)
362
+ - Block and flow style preservation
363
+ - Anchor and alias support
364
+ - Multi-document streams (`load_all` / `dump_all`)
365
+ - Literal (`|`) and folded (`>`) block scalars
366
+ - All standard YAML types: `str`, `int`, `float`, `bool`, `null`, `datetime`, `binary`
367
+ - Octal (`0o777`) and hexadecimal (`0xFF`) integer literals
368
+ - `inf`, `-inf`, `.nan` float values
369
+ - Full type annotations, mypy `--strict` clean
370
+ - PEP 561 typed package
371
+ - Zero dependencies
372
+
373
+ ---
374
+
375
+ ## License
376
+
377
+ [MIT](LICENSE)
@@ -0,0 +1,97 @@
1
+ # yamlsmith — Round-Trip YAML 1.2 Library for Python
2
+
3
+ ## Target Library
4
+
5
+ **ruamel.yaml** — the only production-ready Python library for round-trip YAML editing with comment preservation.
6
+
7
+ | Metric | Value |
8
+ |--------|-------|
9
+ | Downloads | ~155M/month |
10
+ | Maintainers | 1 (Anthon van der Neut) |
11
+ | Dependent packages | 3,080 |
12
+ | Latest release | v0.19.1 (Jan 2026) |
13
+ | Hosting | SourceForge/Mercurial |
14
+ | Community fork | ruyaml (stalled, no releases 12+ months) |
15
+
16
+ ### Why Replace
17
+
18
+ - **Bus factor = 1.** Single maintainer with no governance structure.
19
+ - **PEP 625 crisis.** Maintainer signaled potential inability to continue uploading to PyPI due to namespace package naming requirements.
20
+ - **Hostile contribution model.** SourceForge/Mercurial hosting makes PRs/community contribution nearly impossible.
21
+ - **Failed fork.** ruyaml (pycontribs) was created specifically to address these risks and has itself stalled.
22
+ - **No alternative.** PyYAML does not support round-trip editing or comment preservation. There is no production-ready substitute.
23
+
24
+ ## Package Name
25
+
26
+ **yamlsmith** — verified available on PyPI.
27
+
28
+ ## Scope
29
+
30
+ A modern Python library for YAML 1.2 parsing and emitting with full round-trip fidelity: comments, ordering, formatting, and whitespace are preserved through load/modify/dump cycles.
31
+
32
+ ## Architecture
33
+
34
+ ### Core Components
35
+
36
+ 1. **Scanner/Tokenizer** — Stream-based YAML 1.2 tokenizer that captures comments and whitespace as metadata tokens.
37
+ 2. **Parser** — Produces an event stream (similar to SAX) from tokens, attaching comment metadata.
38
+ 3. **Composer** — Builds a document tree (node graph) from the event stream, with anchor/alias resolution.
39
+ 4. **Representer** — Maps Python objects to YAML nodes, preserving type information.
40
+ 5. **Constructor** — Maps YAML nodes back to Python objects.
41
+ 6. **Emitter** — Serializes the node graph back to YAML text, replaying preserved comments and formatting.
42
+ 7. **Public API** — Simple `load()`, `dump()`, `load_all()`, `dump_all()` functions plus a `YAML` class for configuration.
43
+
44
+ ### Key Design Decisions
45
+
46
+ - **Pure Python, zero dependencies.** No C extensions, no Rust. Simple `pip install`.
47
+ - **YAML 1.2 only.** No YAML 1.1 legacy support (boolean `yes/no/on/off` quirks).
48
+ - **Round-trip by default.** The primary API preserves comments and formatting. Safe-load semantics by default (no arbitrary Python object construction).
49
+ - **Type-annotated.** Full PEP 561 type stubs, mypy-clean.
50
+ - **Python 3.10+.**
51
+
52
+ ### API Surface
53
+
54
+ ```python
55
+ from yamlsmith import YAML
56
+
57
+ yaml = YAML()
58
+
59
+ # Round-trip load and dump
60
+ data = yaml.load(text)
61
+ yaml.dump(data, stream)
62
+
63
+ # Convenience functions
64
+ from yamlsmith import load, dump, load_all, dump_all
65
+
66
+ data = load(text) # round-trip mode
67
+ text = dump(data) # preserves comments
68
+ ```
69
+
70
+ ### Comment Preservation Strategy
71
+
72
+ Comments attach to the nearest node:
73
+ - **Pre-comments:** Lines before a mapping key or sequence item.
74
+ - **Inline comments:** `# comment` on the same line as a value.
75
+ - **Post-comments:** Trailing comments after a block.
76
+ - **Document-level:** Comments before `---` or after `...`.
77
+
78
+ Comments are stored as metadata on the node objects, not in a separate side-channel.
79
+
80
+ ## Deliverables
81
+
82
+ 1. Core YAML 1.2 scanner, parser, composer, emitter with comment preservation
83
+ 2. Python object construction/representation (dict, list, str, int, float, bool, None, datetime, binary)
84
+ 3. Round-trip API (`YAML` class + convenience functions)
85
+ 4. Anchor/alias support
86
+ 5. Multi-document support (`load_all` / `dump_all`)
87
+ 6. Flow style vs block style preservation
88
+ 7. Comprehensive test suite (YAML Test Suite compliance)
89
+ 8. PyPI package with PEP 561 type stubs
90
+ 9. README with migration guide from ruamel.yaml
91
+
92
+ ## Non-Goals (v1)
93
+
94
+ - YAML 1.1 compatibility mode
95
+ - Custom Python object serialization (no `!!python/object`)
96
+ - C extension acceleration
97
+ - Schema validation