SPB Git

spb/ultra-sharp-agent-skills Public

Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.

Python 100%

# YAML Recipes

# Contents

  • Create a config from scratch
  • Read: multi-document files and defaults
  • Modify: nested keys, lists, comment-preserving edits
  • Validate against a schema
  • Convert YAML ↔ JSON
  • Gotchas (Norway problem, implicit typing, anchors)

# Create a config from scratch

python
import yaml

config = {
    "server": {"host": "0.0.0.0", "port": 8080},
    "features": ["auth", "metrics"],
    "welcome": "Bienvenue à Montréal",
}
with open("config.yaml", "w") as f:
    yaml.safe_dump(config, f,
                   default_flow_style=False,  # block style, not {a: 1}
                   sort_keys=False,           # keep insertion order
                   allow_unicode=True)        # keep accented chars readable

Force quotes on an ambiguous scalar:

python
yaml.safe_dump({"country": "NO", "version": "1.10"}, f,
               default_style=None)  # safe_dump already quotes these on output
# When hand-writing YAML text, quote them yourself: country: "NO"

# Read: multi-document files and defaults

python
import yaml

# Single document
data = yaml.safe_load(open("config.yaml")) or {}   # empty file → None, so `or {}`

# Multi-document (--- separators), e.g. Kubernetes manifests
docs = list(yaml.safe_load_all(open("manifests.yaml")))

# Nested read with defaults
port = (data.get("server") or {}).get("port", 8080)

# Modify: nested keys, lists, comment-preserving edits

PyYAML (comments will be lost):

python
data = yaml.safe_load(open("config.yaml"))
data.setdefault("server", {})["port"] = 9090
data.setdefault("features", []).append("tracing")
yaml.safe_dump(data, open("config.yaml", "w"),
               default_flow_style=False, sort_keys=False, allow_unicode=True)

ruamel.yaml (comments, quotes, key order all survive):

python
from ruamel.yaml import YAML

y = YAML()                 # round-trip mode
y.indent(mapping=2, sequence=4, offset=2)   # match common k8s/compose style
doc = y.load(open("docker-compose.yml"))
doc["services"]["web"]["ports"] = ["8080:80"]
y.dump(doc, open("docker-compose.yml", "w"))

Multi-document write:

python
yaml.safe_dump_all(docs, open("manifests.yaml", "w"),
                   default_flow_style=False, sort_keys=False)

# Validate against a schema

For structural guarantees use jsonschema (pip install jsonschema) on the loaded data — YAML loads to the same shapes JSON Schema describes:

python
import yaml, jsonschema

schema = {
    "type": "object",
    "required": ["server"],
    "properties": {
        "server": {
            "type": "object",
            "required": ["port"],
            "properties": {"port": {"type": "integer",
                                    "minimum": 1, "maximum": 65535}},
        }
    },
}
data = yaml.safe_load(open("config.yaml"))
jsonschema.validate(data, schema)   # raises ValidationError with a JSON path

# Convert YAML ↔ JSON

python
import json, yaml

# YAML → JSON
json.dump(yaml.safe_load(open("config.yaml")), open("config.json", "w"),
          indent=2, ensure_ascii=False)

# JSON → YAML
yaml.safe_dump(json.load(open("config.json")), open("config.yaml", "w"),
               default_flow_style=False, sort_keys=False, allow_unicode=True)

Caveat: JSON has no equivalent for YAML anchors, multi-doc streams, or non-string keys — conversion resolves/loses them.

# Gotchas (Norway problem, implicit typing, anchors)

  • The Norway problem. In YAML 1.1 (what PyYAML implements), unquoted no, yes, on, off, y, n parse as booleans — country: NO becomes country: False. Quote them. Same family: port: 022 is octal 18, version: 1.10 is the float 1.1.
  • Timestamps auto-convert. date: 2026-08-05 loads as datetime.date, not a string. Quote if you need the text.
  • safe_load of an empty file returns None, not {} — guard with or {}.
  • Duplicate keys don't error in PyYAML — the last one silently wins. ruamel.yaml raises; use it when duplicates would be a bug.
  • Anchors/aliases (&base, *base, <<: merge) load fine but PyYAML re-dumps them expanded (aliases only re-emitted when the same object identity repeats). If the user's file relies on anchors for maintainability, edit with ruamel.yaml.
  • sort_keys defaults to True in safe_dump — it silently alphabetizes configs; always pass sort_keys=False when round-tripping.
  • Long strings get folded with line breaks on dump; pass width=4096 to keep long values (URLs, tokens) on one line.
  • YAML 1.2 vs 1.1. ruamel.yaml follows 1.2 (only true/false are booleans); PyYAML follows 1.1. The same file can load differently across the two libraries — pick one per task and stay with it.