Skip to content

Commit f344697

Browse files
committed
Clarify Yaml-only wrapper usage and add examples.
1 parent 8941bc6 commit f344697

File tree

12 files changed

+207
-196
lines changed

12 files changed

+207
-196
lines changed

docs/api.md

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# API Reference
22

3-
The `yaml12` module exposes parsing/formatting helpers plus two small dataclasses. Arguments mirror the Rust bindings and stay strict so type errors surface early.
3+
The `yaml12` module exposes parsing/formatting helpers plus a single node wrapper `Yaml` (also exported as `Tagged` and `MappingKey` for compatibility). Use plain Python types whenever possible; reach for `Yaml` only when a node is tagged or when a collection appears as a mapping key. Arguments mirror the Rust bindings and stay strict so type errors surface early.
44

55
## parse_yaml(text, multi=False, handlers=None)
66

77
- `text`: `str` or sequence of `str`; sequence items are joined with newlines.
88
- `multi`: when `True`, parses the whole stream into a list (empty input yields `[]`); otherwise returns only the first document or `None` for empty input.
99
- `handlers`: optional `dict[str, Callable]` mapping tag strings to callables; handlers apply to tagged values and keys and run on already-parsed Python values.
10-
- Returns parsed Python values. Non-core tags (and informative core tags like `!!timestamp`, `!!binary`, `!!set`, `!!omap`, `!!pairs`, or non-specific `!`) become `Tagged` when no handler matches. Scalar core tags (`!!str`, `!!int`, `!!bool`, `!!float`, `!!null`, `!!seq`, `!!map`) are normalized to plain Python types.
11-
- Unhashable mapping keys (sequences/mappings) are wrapped in `MappingKey` so they can be hashable.
10+
- Returns parsed Python values. Tagged nodes (non-core tags plus informative core tags like `!!timestamp`, `!!binary`, `!!set`, `!!omap`, `!!pairs`, or non-specific `!`) become `Yaml` objects when no handler matches; scalar core tags (`!!str`, `!!int`, `!!bool`, `!!float`, `!!null`, `!!seq`, `!!map`) are normalized to plain Python types.
11+
- Unhashable mapping keys (sequences/mappings, or tagged collections) become `Yaml(value, tag=None)` so they remain hashable; tagged scalar keys become `Yaml(value, tag)`.
1212
- Raises `ValueError` on YAML parse errors or invalid tag strings; `TypeError` on wrong argument types; handler exceptions propagate unchanged.
1313

1414
## read_yaml(path, multi=False, handlers=None)
@@ -19,7 +19,7 @@ The `yaml12` module exposes parsing/formatting helpers plus two small dataclasse
1919

2020
## format_yaml(value, multi=False)
2121

22-
- Serializes a Python value (or list of documents when `multi=True`) into a YAML string. `Tagged` values keep their tags unless they target core scalar tags; `MappingKey` preserves complex mapping keys.
22+
- Serializes a Python value (or list of documents when `multi=True`) into a YAML string. `Yaml` nodes keep their tags unless they target core scalar tags; wrapping unhashable mapping keys in `Yaml` preserves them.
2323
- When `multi=False`, the returned string omits the leading document marker and trailing newline. When `multi=True`, the string starts with `---\n` and ends with `...\n`; an empty sequence emits `---\n...\n`.
2424
- Raises `TypeError` if `multi=True` and `value` is not a sequence, or when unsupported types are provided.
2525

@@ -30,18 +30,36 @@ The `yaml12` module exposes parsing/formatting helpers plus two small dataclasse
3030
- Writers are tried with text first and retried as bytes if needed.
3131
- Raises `IOError` on write failures or `TypeError` when inputs are invalid.
3232

33-
## Tagged
33+
## Yaml (aliases: Tagged, MappingKey)
3434

3535
Frozen `dataclass` with fields:
3636

3737
- `value`: the parsed Python value.
38-
- `tag`: the rendered tag string (for example `"!foo"` or `"tag:yaml.org,2002:timestamp"`).
38+
- `tag`: the rendered tag string (for example `"!foo"` or `"tag:yaml.org,2002:timestamp"`), or `None` when used solely to wrap an unhashable mapping key.
3939

40-
Produced for non-core tags (and informative core tags such as timestamps or binary) when no handler applies. You can construct it manually when emitting YAML to preserve tags; core scalar tags are stripped during serialization because they carry no extra information.
40+
Produced for non-core tags (and informative core tags such as timestamps or binary) when no handler applies, and for any unhashable mapping key. You can construct it manually when emitting YAML to preserve tags or wrap complex keys; core scalar tags are stripped during serialization because they carry no extra information.
4141

42-
## MappingKey
42+
### Usage examples
4343

44-
Frozen `dataclass` that wraps unhashable mapping keys (sequences, mappings, or tagged values). Equality and hashing use a frozen structural view of `value`, and it proxies indexing/iteration/len to the wrapped object when applicable. Use it to emit or compare YAML mappings that rely on complex keys.
44+
```python
45+
from yaml12 import Yaml, format_yaml, parse_yaml
46+
47+
# Tagged scalar
48+
text = format_yaml({"env": Yaml("prod", "!env")})
49+
assert "!env" in text
50+
assert isinstance(parse_yaml(text)["env"], Yaml)
51+
52+
# Collection key
53+
data = {Yaml(["a", "b"]): "val"}
54+
out = format_yaml(data)
55+
assert parse_yaml(out)[Yaml(["a", "b"])] == "val"
56+
57+
# Tagged collection key
58+
data = {Yaml(["a", "b"], "!pair"): "val"}
59+
out = format_yaml(data)
60+
key = next(iter(parse_yaml(out)))
61+
assert isinstance(key, Yaml) and key.tag == "!pair" and key.value == ["a", "b"]
62+
```
4563

4664
## _dbg_yaml(text)
4765

docs/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# yaml12
22

3-
`yaml12` exposes the Rust-based `saphyr` YAML 1.2 parser and emitter to Python through a small, function-first API. The bindings keep conversions lean, support multi-document streams, preserve non-core tags through a lightweight `Tagged` dataclass, and round-trip complex mapping keys via `MappingKey`.
3+
`yaml12` exposes the Rust-based `saphyr` YAML 1.2 parser and emitter to Python through a small, function-first API. The bindings keep conversions lean, support multi-document streams, and materialize tagged nodes or unhashable mapping keys as a single lightweight `Yaml` dataclass (also exported as `Tagged` and `MappingKey`); otherwise you just use plain Python types.
44

55
- Parse YAML text or files into familiar Python types with `parse_yaml` and `read_yaml`; handlers apply to both values and keys.
66
- Serialize Python values back to YAML with `format_yaml` or write directly to disk/stdout with `write_yaml`, including non-core tags.
7-
- Round-trip unhashable mapping keys using `MappingKey` to keep tagged collections, sequences, and mappings stable; tagged scalar keys stay plain `Tagged`.
7+
- Round-trip tagged nodes and unhashable mapping keys using `Yaml`, keeping tagged scalars, tagged collections, and bare collections stable across parse/format.
88

99
## Installation
1010

docs/tags.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@
22

33
YAML 1.2 allows tagging values with custom semantics. `yaml12` preserves those tags by default and lets you plug in your own coercions.
44

5-
## Tagged wrapper
5+
## Yaml wrapper
66

7-
Non-core tags that do not have a matching handler are wrapped in a frozen `Tagged` dataclass:
7+
Non-core tags that do not have a matching handler are wrapped in a frozen `Yaml` dataclass (also exported as `Tagged`):
88

99
```python
10-
from yaml12 import Tagged, parse_yaml
10+
from yaml12 import Yaml, parse_yaml
1111

1212
color = parse_yaml("!color red")
13-
assert isinstance(color, Tagged)
13+
assert isinstance(color, Yaml)
1414
assert color.value == "red"
1515
assert color.tag == "!color"
1616
```
1717

18-
`Tagged` works for both scalar and collection nodes, including keys in mappings. You can serialize tagged values by passing a `Tagged` instance back into `format_yaml` or `write_yaml`. Non-specific tags (`!`) produce `Tagged("value", "!")` unless you supply a handler. Tagged scalar keys stay plain `Tagged`; tagged collections are wrapped in `MappingKey(Tagged(...))` so they remain hashable.
18+
`Yaml` works for both scalar and collection nodes, including keys in mappings. You can serialize tagged values by passing a `Yaml` instance back into `format_yaml` or `write_yaml`. Non-specific tags (`!`) produce `Yaml("value", "!")` unless you supply a handler. Tagged scalar keys become `Yaml(value, tag)`; tagged collections and untagged collections used as keys are wrapped in `Yaml(value, tag)` (with `tag=None` for untagged collections).
1919

20-
Core scalar tags (`!!str`, `!!int`, `!!float`, `!!bool`, `!!null`, `!!seq`, `!!map`) are normalized to plain Python values instead of `Tagged`. Informative core tags (such as `!!timestamp`, `!!binary`, `!!set`, `!!omap`, or `!!pairs`) stay tagged so you can choose whether to handle them or preserve them verbatim. Invalid values for core tags raise `ValueError`.
20+
Core scalar tags (`!!str`, `!!int`, `!!float`, `!!bool`, `!!null`, `!!seq`, `!!map`) are normalized to plain Python values instead of `Yaml`. Informative core tags (such as `!!timestamp`, `!!binary`, `!!set`, `!!omap`, or `!!pairs`) stay tagged so you can choose whether to handle them or preserve them verbatim. Invalid values for core tags raise `ValueError`.
2121

2222
## Handler functions
2323

docs/usage.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Usage
22

3-
The Python surface of `yaml12` is intentionally small: four functions that parse and emit YAML plus helpers for non-core tags (`Tagged`) and complex mapping keys (`MappingKey`).
3+
The Python surface of `yaml12` is intentionally small: four functions that parse and emit YAML plus a single `Yaml` helper (also exported as `Tagged`/`MappingKey`) that you only need for two cases: tagged nodes and collection keys.
44

55
## Parse YAML text
66

@@ -15,7 +15,7 @@ assert config["items"] == ["rust", "python"]
1515

1616
- Set `multi=True` to parse a stream of documents into a list; `multi=False` returns only the first document and ignores the rest of the stream.
1717
- Supply `handlers` (a `dict` mapping tag strings to callables) to coerce specific tags on the fly. Handlers see already-parsed Python values and run for tagged keys as well as values. See [Custom Tags](tags.md) for details.
18-
- Non-core tags without handlers become `Tagged`. Unhashable mapping keys (lists/dicts or tagged collections) are wrapped in `MappingKey` so they remain usable as dictionary keys; tagged scalar keys remain plain `Tagged`.
18+
- Non-core tags without handlers become `Yaml(tag=..., value=...)`. Unhashable mapping keys (lists/dicts or tagged collections) become `Yaml(value, tag=None)` so they remain usable as dictionary keys; tagged scalar keys become `Yaml` with the tag preserved.
1919

2020
## Read from files
2121

@@ -32,7 +32,7 @@ File I/O errors raise `IOError`. Parse problems surface as `ValueError`. Invalid
3232

3333
## Emit YAML text
3434

35-
`format_yaml(value, multi=False)` serializes a Python value (or list of documents when `multi=True`) into YAML text. Tagged values retain their tags unless they target core scalar tags like `!!int` or `!!str`. Unhashable mapping keys stay wrapped in `MappingKey`.
35+
`format_yaml(value, multi=False)` serializes a Python value (or list of documents when `multi=True`) into YAML text. `Yaml` values retain their tags unless they target core scalar tags like `!!int` or `!!str`. Unhashable mapping keys stay wrapped in `Yaml(value, tag=None)`.
3636

3737
```python
3838
from yaml12 import format_yaml
@@ -64,4 +64,23 @@ write_yaml(["first", "second"], path="out.yml", multi=True)
6464

6565
## Complex mapping keys
6666

67-
YAML allows sequences, mappings, and tagged scalars as keys. Parsed results wrap any unhashable key (collections or tagged collections) in a `MappingKey` so it can live in a Python `dict` while preserving equality and hashing by structure. Tagged scalar keys remain plain `Tagged`. To emit an unhashable key, wrap it in `MappingKey` (for tagged collections, wrap the `Tagged` value itself) before passing it to `format_yaml` or `write_yaml`.
67+
YAML allows sequences, mappings, and tagged scalars as keys. Parsed results wrap any unhashable key (collections or tagged collections) in `Yaml(value, tag=None)` so it can live in a Python `dict` while preserving equality and hashing by structure. Tagged scalar keys remain tagged `Yaml` nodes. To emit an unhashable key, wrap it in `Yaml` (for tagged collections, set `Yaml(value, tag)`), then pass it to `format_yaml` or `write_yaml`.
68+
69+
## Quick `Yaml` examples
70+
71+
```python
72+
from yaml12 import Yaml, format_yaml, parse_yaml
73+
74+
# Tagged value
75+
text = format_yaml({"mode": Yaml("prod", "!mode")})
76+
assert parse_yaml(text)["mode"].tag == "!mode"
77+
78+
# Untagged collection key
79+
mapping = {Yaml(["a", "b"]): "v"}
80+
assert parse_yaml(format_yaml(mapping))[Yaml(["a", "b"])] == "v"
81+
82+
# Tagged collection key
83+
mapping = {Yaml(["a", "b"], "!pair"): "v"}
84+
key = next(iter(parse_yaml(format_yaml(mapping))))
85+
assert key.tag == "!pair" and key.value == ["a", "b"]
86+
```

0 commit comments

Comments
 (0)