A modern YAML 1.2 parser and emitter for Python, written in Rust
See the codeA YAML 1.2 parser/formatter for Python, implemented in Rust for speed
and correctness. Built on the excellent
saphyr crate.
Compared with YAML 1.1's type library, YAML 1.2's recommended core schema uses more conservative type inference for unquoted values. The YAML 1.1 versus 1.2 quick reference summarizes the differences.
For almost every use case, yaml12 lets you work with plain builtin
Python types end to end: dict, list, int, float, str, and
None. JSON is a subset of YAML 1.2, so all valid JSON is also valid
YAML and parses the same way.
parse_yaml() and read_yaml().format_yaml() or write_yaml().Yaml is the wrapper type for tagged nodes and unhashable
mapping keys.The package ships prebuilt wheels for CPython 3.10+ and PyPy 3.11. Install from PyPI:
pip install py-yaml12
Wheel coverage:
| Platform | CPython architectures | PyPy 3.11 architectures |
|---|---|---|
| Linux (glibc) | x86-64, x86, ARM64, ARMv7, ppc64le, s390x | x86-64, x86 |
| Linux (musl) | x86-64, x86, ARM64, ARMv7 | — |
| macOS | Apple silicon, Intel | Apple silicon, Intel |
| Windows | x86-64, x86, ARM64 | x86-64 |
CPython 3.14 free-threaded builds use separate cp314t wheels for the
same CPython platforms. Free-threading support is currently beta.
You can install the development version of yaml12 from github.
Clone the repository and install in editable mode:
git clone https://github.com/posit-dev/py-yaml12.git
cd py-yaml12
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install --group test
pip install -e . --no-build-isolation
To install the latest main branch without cloning:
pip install git+https://github.com/posit-dev/py-yaml12.git
Python 3.10 or newer is required. Building from source also requires Rust 1.83 or newer.
from yaml12 import parse_yaml, format_yaml, Yaml
yaml_text = """
title: A modern YAML parser and emitter written in Rust
properties: [fast, correct, safe, simple]
features:
tags: preserve
streams: multi
"""
doc = parse_yaml(yaml_text)
assert doc == {
"title": "A modern YAML parser and emitter written in Rust",
"properties": ["fast", "correct", "safe", "simple"],
"features": {"tags": "preserve", "streams": "multi"},
}
round_tripped = parse_yaml(format_yaml(doc))
assert round_tripped == doc
# Long strings wrap at 80 columns by default.
narrow_yaml = format_yaml(doc, width=40)
# None disables wrapping.
unwrapped_yaml = format_yaml(doc, width=None)
# Tagged values (advanced)
from yaml12 import Yaml
tagged = parse_yaml("!expr 1 + 1")
assert tagged == Yaml(value="1 + 1", tag="!expr")
Filesystem paths beginning with ~ are expanded using os.path.expanduser().
from yaml12 import read_yaml, write_yaml
value_out = {"alpha": 1, "nested": [True, None]}
write_yaml(value_out, "my.yaml")
value_in = read_yaml("my.yaml")
assert value_in == value_out
# Append another YAML document instead of replacing the file.
next_value = {"beta": 2}
write_yaml(next_value, "my.yaml", append=True)
assert read_yaml("my.yaml", multi=True) == [value_out, next_value]
# Multi-document streams
docs_out = [{"foo": 1}, {"bar": [2, None]}]
write_yaml(docs_out, "my-multi.yaml", multi=True)
docs_in = read_yaml("my-multi.yaml", multi=True)
assert docs_in == docs_out
Handlers let you opt into custom behavior for tagged nodes while keeping the default parser strict and safe.
from yaml12 import parse_yaml
yaml_text = """
- !upper [rust, python]
- !expr 6 * 7
"""
handlers = {
"!expr": lambda value: eval(value),
"!upper": lambda value: [x.upper() for x in value],
}
doc = parse_yaml(yaml_text, handlers=handlers)
assert doc == [["RUST", "PYTHON"], 42]
YAML mappings can use keys that themselves collections, or that carry
tags. Such keys cannot always be represented directly in a Python
dict, so yaml12 wraps them in Yaml to make the key hashable.
from yaml12 import Yaml, parse_yaml, format_yaml
obj = {
"seq": [1, 2],
"map": {"key": "value"},
"tagged": Yaml("1 + 1", "!expr"),
Yaml("foo", "!custom-key"): "bar",
}
yaml_text = format_yaml(obj)
round_tripped = parse_yaml(yaml_text)
assert round_tripped == obj
Online docs: https://posit-dev.github.io/py-yaml12/
To build or serve the docs locally:
pip install --group docs
pip install --upgrade great-docs
great-docs build # or: great-docs preview
From the repo root:
cargo fmt
cargo check
cargo test
cargo build
cargo clippy
.venv/bin/pip install -e . --no-build-isolation
.venv/bin/python -m pytest tests_py
See the release checklist for the version, artifact, tag, and publication checklist.
932 followers · starred Aug 2026
329 followers · starred Jun 2026
171 followers · starred May 2026
156 followers · starred Mar 2026
A modern YAML 1.2 parser and emitter for Python, written in Rust
See the codeA YAML 1.2 parser/formatter for Python, implemented in Rust for speed
and correctness. Built on the excellent
saphyr crate.
Compared with YAML 1.1's type library, YAML 1.2's recommended core schema uses more conservative type inference for unquoted values. The YAML 1.1 versus 1.2 quick reference summarizes the differences.
For almost every use case, yaml12 lets you work with plain builtin
Python types end to end: dict, list, int, float, str, and
None. JSON is a subset of YAML 1.2, so all valid JSON is also valid
YAML and parses the same way.
parse_yaml() and read_yaml().format_yaml() or write_yaml().Yaml is the wrapper type for tagged nodes and unhashable
mapping keys.The package ships prebuilt wheels for CPython 3.10+ and PyPy 3.11. Install from PyPI:
pip install py-yaml12
Wheel coverage:
| Platform | CPython architectures | PyPy 3.11 architectures |
|---|---|---|
| Linux (glibc) | x86-64, x86, ARM64, ARMv7, ppc64le, s390x | x86-64, x86 |
| Linux (musl) | x86-64, x86, ARM64, ARMv7 | — |
| macOS | Apple silicon, Intel | Apple silicon, Intel |
| Windows | x86-64, x86, ARM64 | x86-64 |
CPython 3.14 free-threaded builds use separate cp314t wheels for the
same CPython platforms. Free-threading support is currently beta.
You can install the development version of yaml12 from github.
Clone the repository and install in editable mode:
git clone https://github.com/posit-dev/py-yaml12.git
cd py-yaml12
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install --group test
pip install -e . --no-build-isolation
To install the latest main branch without cloning:
pip install git+https://github.com/posit-dev/py-yaml12.git
Python 3.10 or newer is required. Building from source also requires Rust 1.83 or newer.
from yaml12 import parse_yaml, format_yaml, Yaml
yaml_text = """
title: A modern YAML parser and emitter written in Rust
properties: [fast, correct, safe, simple]
features:
tags: preserve
streams: multi
"""
doc = parse_yaml(yaml_text)
assert doc == {
"title": "A modern YAML parser and emitter written in Rust",
"properties": ["fast", "correct", "safe", "simple"],
"features": {"tags": "preserve", "streams": "multi"},
}
round_tripped = parse_yaml(format_yaml(doc))
assert round_tripped == doc
# Long strings wrap at 80 columns by default.
narrow_yaml = format_yaml(doc, width=40)
# None disables wrapping.
unwrapped_yaml = format_yaml(doc, width=None)
# Tagged values (advanced)
from yaml12 import Yaml
tagged = parse_yaml("!expr 1 + 1")
assert tagged == Yaml(value="1 + 1", tag="!expr")
Filesystem paths beginning with ~ are expanded using os.path.expanduser().
from yaml12 import read_yaml, write_yaml
value_out = {"alpha": 1, "nested": [True, None]}
write_yaml(value_out, "my.yaml")
value_in = read_yaml("my.yaml")
assert value_in == value_out
# Append another YAML document instead of replacing the file.
next_value = {"beta": 2}
write_yaml(next_value, "my.yaml", append=True)
assert read_yaml("my.yaml", multi=True) == [value_out, next_value]
# Multi-document streams
docs_out = [{"foo": 1}, {"bar": [2, None]}]
write_yaml(docs_out, "my-multi.yaml", multi=True)
docs_in = read_yaml("my-multi.yaml", multi=True)
assert docs_in == docs_out
Handlers let you opt into custom behavior for tagged nodes while keeping the default parser strict and safe.
from yaml12 import parse_yaml
yaml_text = """
- !upper [rust, python]
- !expr 6 * 7
"""
handlers = {
"!expr": lambda value: eval(value),
"!upper": lambda value: [x.upper() for x in value],
}
doc = parse_yaml(yaml_text, handlers=handlers)
assert doc == [["RUST", "PYTHON"], 42]
YAML mappings can use keys that themselves collections, or that carry
tags. Such keys cannot always be represented directly in a Python
dict, so yaml12 wraps them in Yaml to make the key hashable.
from yaml12 import Yaml, parse_yaml, format_yaml
obj = {
"seq": [1, 2],
"map": {"key": "value"},
"tagged": Yaml("1 + 1", "!expr"),
Yaml("foo", "!custom-key"): "bar",
}
yaml_text = format_yaml(obj)
round_tripped = parse_yaml(yaml_text)
assert round_tripped == obj
Online docs: https://posit-dev.github.io/py-yaml12/
To build or serve the docs locally:
pip install --group docs
pip install --upgrade great-docs
great-docs build # or: great-docs preview
From the repo root:
cargo fmt
cargo check
cargo test
cargo build
cargo clippy
.venv/bin/pip install -e . --no-build-isolation
.venv/bin/python -m pytest tests_py
See the release checklist for the version, artifact, tag, and publication checklist.
932 followers · starred Aug 2026
329 followers · starred Jun 2026
171 followers · starred May 2026
156 followers · starred Mar 2026