YAML-io
This Python library enables YAML anchors to be imported from external files via custom directives. It also enables imported anchors to be re-exported, so one file can unify many imports and then export them as one source.
This has a few benefits:
- Re-use the same aliases in multiple files without repeating yourself
- Use aliases without worrying about reordering your file, because the anchors are defined elsewhere
- Use multiple layers of imports to logically separate concerns and create versions of files that update all downstream files simultaneously
Install
pip install yaml-io
Requires Python 3.11+ and PyYAML 5.4+.
Usage
import yaml_io
data = yaml_io.load("workspace/acme/actions.yml")
yaml_io.load accepts a path, an open file object, or a YAML string. For multi-document streams, use yaml_io.load_all, which returns a list.
Importing yaml_io has no side effects. It does not patch PyYAML, so yaml.safe_load and everything else in your process behaves exactly as it always did.
If you would rather go through PyYAML directly, pass the loader explicitly:
import yaml
import yaml_io
with open("workspace/acme/actions.yml") as f:
data = yaml.load(f, Loader=yaml_io.YamlIOLoader)
YamlIOLoader is built on PyYAML's SafeConstructor, so it is as safe as yaml.safe_load.
Directives
Mark directives in your YAML like:
#!import ../../global/prod-default/base-package.yml as base
and refer to imported anchors as:
- *base.progress_check_model
Anchors defined directly in a file are exported automatically. Imported anchors are not: to pass one further downstream, re-export it explicitly.
#!export base.progress_check_model
A re-exported anchor loses its original prefix and picks up the prefix of whoever imports it next. Export several at once with commas, or use * to re-export everything from one import:
#!export base.model_a, base.model_b
#!export base.*
Rules worth knowing:
- Import paths are relative to the file containing the directive.
- Directives must start at column 0. Anything indented is an ordinary YAML comment, so
#!importinside a block scalar is left alone. - Paths containing spaces must be quoted:
#!import "my dir/base.yml" as base. - Directives are file-scoped. In a multi-document file, imported anchors are available in every document, while anchors defined inside a document stay in that document, matching YAML's own scoping.
- Anchor names (
&name) may not contain dots. Aliases (*prefix.name) may contain exactly one.
Versioning Example
A versioned file containing all the anchors we want to define once. Imagine we have multiple files and many anchors.
# ./global/versions/v1.2/actions.yml
defs:
one: &anchor1 {name: first, retries: 3}
two: &anchor2 {name: second, retries: 5}
A router-like file where we refer to the versioned files and export them for re-use:
# ./global/prod-default/base-package.yml
#!import ../versions/v1.2/actions.yml as a
#!export a.anchor1, a.anchor2
This file has no content of its own, only directives. Loading it directly returns None, which is expected; its job is to forward anchors.
The most downstream file, where all anchors are resolved and used as aliases, such as ./workspace/acme/actions.yml:
# ./workspace/acme/actions.yml
#!import ../../global/prod-default/base-package.yml as base
steps:
- *base.anchor1
- *base.anchor2
Loading that last file yields:
{"steps": [{"name": "first", "retries": 3},
{"name": "second", "retries": 5}]}
-
One advantage to using the
base-packageintermediary is the ability to change everything from v1.2 to v1.3 with a small number of changes when you're ready. -
One advantage to using the
prod-defaultfolder is you can create other folders, such asprod-betaoruat-default, and point specific files there instead for testing.
The same file may be imported along several paths. A diamond, where two intermediaries both import one versioned file, resolves once and is shared.
Errors
All errors inherit from yaml_io.YamlIOError, itself a ValueError.
| Error | Raised when |
|---|---|
CircularImportError |
A file transitively imports itself |
ImportResolutionError |
An #!import target cannot be read |
DirectiveError |
An #!import or #!export line is malformed |
ExportError |
An #!export names an anchor that does not exist |
AnchorCollisionError |
Two distinct anchors compete for one name |
Errors from the YAML itself are PyYAML's own, and report the real filename and the correct line number, since directive lines are blanked rather than removed.
Upgrading from 0.1.x
0.2.0 removes the import-time monkeypatching. Previously import yaml_io reassigned yaml.load and yaml.safe_load, which broke yaml.safe_load for every other consumer in the process. Replace:
import yaml
import yaml_io
with open("some_yaml.yaml") as f:
data = yaml.safe_load(f)
with:
import yaml_io
data = yaml_io.load("some_yaml.yaml")
load_imports_exports is still present and still returns (data, exported), where exported maps names to yaml.Node objects. It is deprecated in favor of yaml_io.load.
Metadata
Release files for yaml-io 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| yaml_io-0.2.0.tar.gz | 15.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| yaml_io-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.5 kB
Release files / yaml_io-0.2.0.tar.gz
| Download URL | yaml_io-0.2.0.tar.gz |
|---|---|
| Size | 15.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
554c258f17d07c659639b4a94e79248b985876b11b4bcd8640953d9bfd668f4f
|
|
BLAKE2b-256 checksum How to use checksums |
00e960b651c0b7d0b608a3065232087351a0c822f460accc674e3d70b4c8d737
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 5, 2026.
Transparency logRelease files / yaml_io-0.2.0-py3-none-any.whl
| Download URL | yaml_io-0.2.0-py3-none-any.whl |
|---|---|
| Size | 11.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8032f513f5272330983632b051601805d8a9e5e3636e661bf7b14c40edfc8320
|
|
BLAKE2b-256 checksum How to use checksums |
517f14dfec9224b7978db6d2820e0538df4ed421bfafb670714ba8e019b91dad
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 5, 2026.
Transparency log