strictyaml review
StrictYAML 1.7.3 imported in 0.20 seconds after our 0.2-second install and occupied 2 MB across 3 packages. It reads a deliberately limited YAML dialect, then validates and converts values with Python schemas such as `Map`, `Seq`, `Int`, and `Enum`. Without a schema, every scalar remains text. Normal loading refuses duplicate keys, flow collections, tags, anchors, and aliases. Returned nodes retain comments and line locations for diagnostics and edits. Release 1.7.3 fixed wheel and source-distribution packaging; it did not introduce a new parser model.
StrictYAML 1.7.3 installed in 0.2 seconds, used 2 MB, imported in 0.20 seconds, and had 0 audit findings in our sandbox. Pick it for Python-owned configuration where rejecting advanced YAML is the policy; use a general parser for interoperable YAML.
We installed it
| Install | ✓ · 0.2s | 3 packages on disk · 2 MB |
| Import | ✓ | import strictyaml in 0.20s · pure Python · requires Python >=3.7.0 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does strictyaml install cleanly?
Yes. In a fresh container with an empty cache, pip install strictyaml finished in 0.2s, leaving 3 packages and 2 MB on disk. pip-audit reported no known vulnerabilities.
What does strictyaml need to run?
Python >=3.7.0, and nothing compiled: it is pure Python. In our run import strictyaml succeeded in 0.20s.
strictyaml or PyYAML: which should you use?
PyYAML: Choose PyYAML for broad producer compatibility and the familiar safe_load dictionary workflow. StrictYAML 1.7.3 installed in 0.2 seconds, used 2 MB, imported in 0.20 seconds, and had 0 audit findings in our sandbox.
When should you not use strictyaml?
Files come from general YAML tools that emit anchors, aliases, tags, or {flow: mappings}. Standard load() refuses those valid YAML features on purpose.
Use it if
- A Python-owned configuration file needs closed schemas that reject missing and unexpected keys with source locations.
- Values such as `yes`, `no`, and `0123` must stay strings until an explicit validator converts them.
- An editor must update validated values while retaining comments and readable block-style YAML.
- Rejecting anchors, tags, duplicate keys, and flow collections is acceptable for every producer of the file.
- Files come from general YAML tools that emit anchors, aliases, tags, or `{flow: mappings}`. Standard `load()` refuses those valid YAML features on purpose.
- One schema must be consumed by Python, JavaScript, and Go services. StrictYAML schemas are Python objects rather than a portable schema document.
- Parsing speed is part of the workload budget. The project's design notes say speed is a lower priority and assume short files read infrequently.
- Your API hands the parser filenames or streams. StrictYAML only accepts decoded YAML strings, leaving file access and encoding errors to caller code.
- Static typing must work from installed package metadata without local stubs. Our 1.7.3 wheel check found no top-level `py.typed` marker.
Setup reality
We installed StrictYAML 1.7.3 in a fresh Python 3.12 Bookworm sandbox in 0.2 seconds. The environment ended with 3 packages using 2 MB on disk. The library is pure Python, requires Python 3.7.0 or newer, and has 1 direct dependency under the MIT license. import strictyaml succeeded in 0.20 seconds, while pip-audit reported 0 known vulnerabilities. The installed package does not advertise typing through a top-level py.typed marker.
Schema choice controls conversion. With no schema, 8080, yes, and dates remain strings. A plain Map demands every listed key and rejects extra ones; Optional relaxes selected keys, and MapPattern checks arbitrary names. Defaults supplied by Optional appear in .data but can be omitted from as_yaml() when they equal the declared default. That difference matters in snapshot tests and generated configuration reviews.
load() accepts decoded text, so open paths yourself and choose the encoding. Keep the YAML node when comments, start_line, or round-trip editing matter; extracting .data gives ordinary Python values without source context. Assigning through a validated node runs the schema again and can raise YAMLSerializationError. The docs also warn that parsed data survives a round trip, while exact newlines or comment placement may shift because of underlying ruamel behavior.
Normal parsing rejects flow style, anchors, aliases, tags, duplicate keys, and inconsistent mapping indentation. dirty_load(..., allow_flow_style=True) accepts flow collections for migration, but it also disables the related indentation-consistency check. Version 1.7.3 fixed package construction for wheels and source archives rather than these parsing rules. The missing top-level typing marker means mypy and pyright users may need local stubs or reduced checking at this boundary.
Patterns
Read every scalar as text load-text-scalars
from strictyaml import load
doc = load('port: 8080\nenabled: yes\n')
assert doc.data == {'port': '8080', 'enabled': 'yes'}Without a schema, both `8080` and `yes` stay strings; StrictYAML performs no implicit scalar typing.
Convert a closed configuration map validate-fixed-map
from strictyaml import Bool, Int, Map, Str, load
schema = Map({
'host': Str(),
'port': Int(),
'debug': Bool(),
})
config = load(yaml_text, schema).dataA plain `Map` rejects an unknown key and also rejects any missing required key before returning data.
Fill an omitted boolean default supply-optional-default
from strictyaml import Bool, Int, Map, Optional, load
schema = Map({
'port': Int(),
Optional('debug', default=False): Bool(),
})
doc = load('port: 8080\n', schema)
assert doc.data['debug'] is FalseThe default appears in `.data`, while `as_yaml()` omits it when the value still equals `False`.
Check every service entry validate-record-sequence
from strictyaml import Int, Map, Seq, Str, load
schema = Seq(Map({'name': Str(), 'port': Int()}))
services = load('- name: api\n port: 8080\n', schema).data`Seq` applies one validator to every item; use `FixedSeq` when position 1 and position 2 need different validators.
Require unique names reject-duplicate-sequence-items
from strictyaml import Str, UniqueSeq, load
schema = UniqueSeq(Str())
names = load('- api\n- worker\n', schema).data`UniqueSeq` raises `YAMLValidationError` on a repeated value during parsing and rejects duplicates during serialization too.
Check user-named port entries validate-dynamic-keys
from strictyaml import Int, MapPattern, Regex, load
schema = MapPattern(Regex(r'^[a-z][a-z0-9-]+$'), Int())
ports = load('api: 8080\nadmin: 8081\n', schema).data`MapPattern` runs one validator against each key and another against each value, with optional minimum and maximum key counts.
Keep required fields beside extensions combine-known-and-extra-keys
from strictyaml import Any, MapCombined, Str, load
schema = MapCombined(
{'name': Str()},
Str(),
Any(),
)
doc = load('name: api\nowner: platform\n', schema)The documentation labels `MapCombined` experimental, so its API may change in a minor 1.x release.
Update a validated value in place edit-preserving-comments
from strictyaml import Int, Map, Str, load
schema = Map({'name': Str(), 'port': Int()})
doc = load('# listener\nname: api\nport: 8080\n', schema)
doc['port'] = 8081
print(doc.as_yaml())Editing the YAML node preserves its comment, while rebuilding from `.data` has no original comment location to reuse.
Put a filename in parse errors label-validation-errors
from pathlib import Path
from strictyaml import YAMLError, load
path = Path('service.yaml')
try:
doc = load(path.read_text(encoding='utf-8'), schema, label=str(path))
except YAMLError as error:
print(error)`label='service.yaml'` replaces the default `<unicode string>` label in line and column diagnostics.
Locate a parsed value inspect-source-lines
doc = load(yaml_text, schema)
port_node = doc['port']
print(port_node.start_line, port_node.end_line)
print(port_node.lines())Line numbers and source text belong to YAML nodes; calling `.data` returns plain Python values without those methods.
Apply a schema after reading related keys revalidate-subtree
from strictyaml import Any, Int, Map, Seq, Str, load
overall = Map({'ports': Any(), 'services': Seq(Str())})
doc = load(yaml_text, overall)
port_schema = Map({name: Int() for name in doc.data['services']})
doc['ports'].revalidate(port_schema)`revalidate()` can change subtree values from strings to integers and rejects any service key absent from the derived schema.
Accept flow collections during migration migrate-flow-style
from strictyaml import Any, Map, dirty_load
schema = Map({'settings': Any()})
doc = dirty_load(
'settings: {port: 8080, debug: no}\n',
schema,
allow_flow_style=True,
)`allow_flow_style=True` admits brace and bracket collections and also skips the related map-indentation consistency check.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| PyYAML | PyPI | Choose PyYAML for broad producer compatibility and the familiar `safe_load` dictionary workflow. |
| ruamel.yaml | PyPI | Choose ruamel.yaml when YAML 1.2 features and detailed round-trip control matter more than a restricted dialect. |
| pydantic | PyPI | Choose Pydantic when one typed model must validate values loaded from YAML, JSON, environment variables, or Python objects. |
| yamale | PyPI | Choose Yamale when the validation schema should live in a separate YAML file instead of executable Python code. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

