Skip to content

YAMLRocks vs ruamel.yaml

ruamel.yaml is the reference for comment-preserving, round-trip YAML in Python. It is excellent and feature-rich, but it is pure Python and therefore slow. YAMLRocks offers the same round-trip fidelity, backed by Rust, at one to two orders of magnitude more throughput, and adds native includes and schema validation. YAMLRocks now scripts comments per node too (inline and leading); ruamel still wins on the most exhaustive comment surgery, covered honestly below.

Featureruamel.yamlYAMLRocks
YAML 1.2YesYes
Comment preservationYesYes
Round-trip (unmodified)CloseByte-for-byte
Anchors/aliases preservedYesYes
Merge keys (<<)YesYes
ImplementationPure PythonRust extension
Speed (parse)baseline~105-141x faster
Speed (dump)baseline~160-208x faster
Native !include + write-backNoYes
Source line/columnpartialYes (annotated mode)
JSON Schema validationNoYes
Save only changed filesNoYes
Per-node comment editing APIYes (.ca, exhaustive)Yes (inline, leading, trailing)
Output typestr (to a stream)bytes

YAMLRocks vs ruamel.yaml on reading and writing: YAMLRocks is one to two orders of magnitude faster on both.

ruamel.yaml is implemented entirely in Python, which makes it flexible but slow. YAMLRocks does the same work in Rust and materializes results across the PyO3 boundary, so the same parse or dump is one to two orders of magnitude faster.

Indicative figures from python bench/bench.py (release build), showing how many times faster YAMLRocks is than ruamel.yaml in safe mode.

Parsing (loads)

PayloadYAMLRocks is
small~105x faster
medium~124x faster
large (500 items)~141x faster
deep~105x faster

Serializing (dumps)

PayloadYAMLRocks is
small~208x faster
medium~201x faster
large~199x faster
deep~163x faster

ruamel’s round-trip mode is heavier still. YAMLRocks’s round-trip path stays far faster while preserving comments, anchors, and formatting, with byte-for-byte output for unmodified documents.

Both libraries preserve comments, anchors, and scalar styles. YAMLRocks goes one step further: an unmodified round-trip reproduces the source bytes exactly, and only the nodes you change are re-rendered. This is enforced across the entire official YAML test suite.

import yamlrocks
source = b"# service config\nname: app # the app name\nport: 8080\n"
# Unmodified: bytes come back exactly as they went in.
doc = yamlrocks.loads(source, option=yamlrocks.OPT_ROUND_TRIP)
assert doc.to_yaml() == source
# Change one value: only that line is re-rendered, comments intact.
doc["port"] = 9090
doc.to_yaml()
# b'# service config\nname: app # the app name\nport: 9090\n'

The editing surface is a YAMLRocksDocument with dict/list-style access plus walk(), to_dict(), and range() helpers, rather than ruamel’s CommentedMap/CommentedSeq types:

import yamlrocks
doc = yamlrocks.loads(b"server:\n host: localhost\n port: 8080\n", option=yamlrocks.OPT_ROUND_TRIP)
doc.keys() # ['server']
doc["server"]["port"] = 9090 # nested edit writes through
doc.to_dict() # {'server': {'host': 'localhost', 'port': 9090}}
doc.walk() # [(('server', 'host'), 'localhost'), (('server', 'port'), 9090)]

See round-trip editing and the config editor recipe.

ruamel.yaml has no !include. A split configuration must be stitched together by hand, and there is no concept of writing an edited value back to the specific file it came from.

YAMLRocks resolves !include and the !include_dir_* family natively. In round-trip mode each node remembers its source file, so save() writes back only the files that actually changed. See includes and the Home Assistant recipe for a worked example of editing one automation and saving only automations.yaml.

YAMLRocks does more than preserve comments through a round-trip: every YAMLRocksNode exposes a writable comment (the inline # ... after a value), comment_before (the standalone line(s) above a key), and comment_after (the trailing block at the foot of a mapping or sequence). A tool can read, add, edit, remove, or move a comment (read it from one node, set it on another) and re-emit. This works on mapping values, keys, and sequence items alike, and on keys you add to a loaded document.

import yamlrocks
doc = yamlrocks.loads(b"name: app\nport: 8080\n", option=yamlrocks.OPT_ROUND_TRIP)
doc.node["port"].comment = "the listen port" # inline, no '#'
doc.node["name"].comment_before = "service identity" # standalone line above
doc.node.comment_after = "end of config" # trailing block (foot)
doc.to_yaml()
# b'# service identity\nname: app\nport: 8080 # the listen port\n# end of config\n'

Building a fully commented document from nothing needs ruamel: YAMLRocks edits comments on a loaded document rather than assembling one with no parsed source. ruamel’s .ca API also reaches a few unusual placements that comment, comment_before, and comment_after do not.

On emitter configuration the two are closer than they once were: YAMLRocks’s dumps takes an explicit width, indentation options (OPT_INDENT_2 / _4, OPT_INDENTLESS_SEQUENCES), a default= hook for serializing custom objects, and a tags= registry, so most representer-style needs are met. ruamel remains more configurable for deeply custom class round-tripping and is battle-tested for intricate document transformations.

Reach for ruamel.yaml when you need to build a fully commented document from scratch, lean on its class-based representer/constructor extension points for deeply custom types, or perform intricate document surgery where its maturity matters more than throughput. For high-throughput loading and dumping, native includes, schema validation, scripting inline, leading, and trailing comments, or byte-for-byte round-trip with file-aware saving, YAMLRocks is the faster, batteries-included choice.