Yet Another YAML AST - programmatically transform YAML, preserving whitespace and comments
Project description
yaya
Yet Another YAML AST - programmatically transform YAML, preserving whitespace and comments
Why?
Ever need to programmatically edit YAML files but want to preserve:
- All comments
- Exact whitespace (including trailing spaces)
- Quote styles
- Block scalar indicators (
|,|-,|+) - Formatting choices
Most YAML libraries (including ruamel.yaml's round-trip mode) make small formatting changes when serializing. yaya solves this by:
- Parsing YAML to get the AST with position information
- Keeping the original bytes
- Applying modifications only to the specific values you change
- Leaving everything else untouched
Installation
pip install lossless-yaml
Usage
Basic String Replacement
from yaya import YAYA
# Load a YAML file
doc = YAYA.load('.github/workflows/test.yaml')
# Simple string replacement in all values
doc.replace_in_values('src/marin', 'lib/marin/src/marin')
# Regex-based replacement
doc.replace_in_values_regex(r'\buv sync(?! --package)', 'uv sync --package myapp')
doc.save()
Path-Based Navigation and Assertions
# Navigate using paths
runs_on = doc.get_path("jobs.test.runs-on")
step_name = doc.get_path("jobs.test.steps[0].name")
# Or dict-like access
runs_on = doc["jobs"]["test"]["runs-on"]
# Assert values before making changes
doc.assert_value("on", ["push"])
doc.assert_absent("jobs.test.defaults")
doc.assert_present("jobs.test.steps")
Replacing Entire Values
# Replace a simple value
doc.replace_key("jobs.test.runs-on", "ubuntu-22.04")
# Replace with a complex structure
doc.replace_key("on", {
"push": {
"branches": ["main"],
"paths": ["lib/**", "uv.lock"]
},
"pull_request": {
"paths": ["lib/**", "uv.lock"]
}
})
doc.save()
Adding New Keys
# Add a key after another key (maintains order)
doc.add_key_after("jobs.test.runs-on", "defaults", {
"run": {
"working-directory": "lib/myapp"
}
})
doc.save()
Example
Given this YAML file:
# Production config
database:
host: prod-db-1.example.com
port: 5432
This code:
doc = YAYA.load('config.yaml')
doc.replace_in_values('prod-db-1', 'prod-db-2')
doc.save()
Produces exactly:
# Production config
database:
host: prod-db-2.example.com
port: 5432
No reformatting. No comment loss. Just the change you made.
How It Works
- Uses
ruamel.yamlto parse YAML and extract position information - Converts line/column positions to byte offsets
- Tracks modifications as you change values
- Applies byte-level replacements when saving
Features
- ✅ Byte-for-byte preservation of unchanged content
- ✅ String replacement (literal and regex)
- ✅ Path-based navigation (
jobs.test.steps[0].name) - ✅ Replace entire values (scalars, dicts, lists)
- ✅ Add new keys with proper positioning
- ✅ Assertions for validation
- ✅ Comment preservation
- ✅ Block scalar support
- ✅ Flow and block style handling
Limitations
- Removing keys not yet implemented
- Binary data in YAML is not supported
- Adding keys at arbitrary positions (only
add_key_aftercurrently)
Comparison with ruamel.yaml
ruamel.yaml is excellent for round-trip YAML editing and preserves most formatting. However:
| Feature | ruamel.yaml | yaya |
|---|---|---|
| Preserves comments | ✅ | ✅ |
| Preserves most whitespace | ✅ | ✅ |
| Byte-for-byte identical | ❌ | ✅ |
| Trailing whitespace | ❌ | ✅ |
| Block scalar indicators | ❌ (computes new ones) | ✅ |
yaya uses ruamel.yaml under the hood but takes a different approach: instead of serializing the AST back to YAML, it modifies the original bytes directly.
License
MIT
Contributing
Issues and pull requests welcome!
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lossless_yaml-0.1.0.tar.gz.
File metadata
- Download URL: lossless_yaml-0.1.0.tar.gz
- Upload date:
- Size: 32.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b30661ae475aefd35e58095478be1edd4225dc90d1c876fd6b5d21a62533c37
|
|
| MD5 |
1615bccfac30e597094f2a62bf3ad7e2
|
|
| BLAKE2b-256 |
830d96ce5108a6f82eba7da1eef9fc96d438c9247772a80b6bec500fc28a6342
|
File details
Details for the file lossless_yaml-0.1.0-py3-none-any.whl.
File metadata
- Download URL: lossless_yaml-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6aff090d8435a9fd6a0d072bf79714e97982d6e1e0a3f7b1b666002328aa6328
|
|
| MD5 |
974f8bf332eb992b93a5175832c4e4cd
|
|
| BLAKE2b-256 |
06b497678d3bee4644d46bf31a3a784c779e09b54010e32feff71f71101edd9e
|