--- name: processing-yaml description: Creates, reads, modifies, and validates YAML files with Python. Use when the user asks to parse, edit, generate, or fix a .yaml or .yml file, mentions YAML syntax errors, or works with YAML-based config files such as docker-compose.yml, Kubernetes manifests, or GitHub Actions workflow files. Do not use for JSON files or for authoring the logic of CI pipelines — only the YAML file mechanics. --- # Processing YAML ## When to use / when NOT to use - **Use for:** creating, reading, editing, or validating `.yaml`/`.yml` files — app configs, docker-compose, Kubernetes manifests, CI workflow files (the file mechanics). - **Do NOT use for:** JSON files (use `json` directly) or designing the *logic* of CI pipelines — this skill covers YAML file handling, not what the pipeline should do. ## Quick reference **Default:** PyYAML (`pip install pyyaml`). **Escape hatch:** ruamel.yaml (`pip install ruamel.yaml`) only when comments and formatting must survive a round-trip edit — PyYAML discards them. ```python import yaml # Read — safe_load ONLY with open("config.yaml") as f: data = yaml.safe_load(f) # returns dict/list/scalars # Modify data["server"]["port"] = 8080 # Write with open("config.yaml", "w") as f: yaml.safe_dump(data, f, default_flow_style=False, sort_keys=False, allow_unicode=True) ``` **Comment-preserving edit (ruamel.yaml):** ```python from ruamel.yaml import YAML y = YAML() # round-trip mode by default doc = y.load(open("config.yaml")) doc["server"]["port"] = 8080 y.dump(doc, open("config.yaml", "w")) ``` ## Rules - **NEVER `yaml.load()` without `SafeLoader`** — it executes arbitrary Python object constructors. `safe_load`/`safe_dump` always. - Quote strings that YAML would reinterpret: `"no"`, `"on"`, `"yes"`, version strings like `"1.10"`, country codes like `"NO"`. - 2-space indentation; never tabs (tabs are a YAML syntax error). - If the file has comments the user wants kept, use ruamel.yaml — a PyYAML round-trip silently deletes them. ## Workflow 1. Determine the operation and whether comments/formatting must survive (→ ruamel.yaml). 2. Parse with `yaml.safe_load`; on `yaml.YAMLError`, report its message with line/column verbatim and stop — do not guess. 3. Modify the loaded structure (recipes in references/recipes.md), quoting ambiguous scalars. 4. Write with `safe_dump(default_flow_style=False, sort_keys=False, allow_unicode=True)` to keep block style, key order, and non-ASCII text. 5. **Validate:** re-parse the written file with `safe_load`; for multi-document files confirm document count is unchanged. Fix and repeat step 3 until clean. ## Edge cases & failure modes - **PyYAML missing** → `pip install pyyaml`; ruamel.yaml missing → `pip install ruamel.yaml`. - **Malformed YAML** → relay the parser error (it includes line/column); common causes to mention: tabs, unquoted `:` in values, bad indentation. - **Multi-document files** (`---` separators) → use `yaml.safe_load_all()` / `yaml.safe_dump_all()`; plain `safe_load` raises on the second document. - **Encoding** → open files as UTF-8; `allow_unicode=True` on dump prevents `\uXXXX` escaping of accented text. - **Huge files** → YAML has no practical streaming parser; for >50 MB data files, question whether the data belongs in YAML at all and suggest JSON/CSV. ## References Deeper copy-paste recipes (anchors, multi-doc, schema validation, JSON conversion): see [references/recipes.md](references/recipes.md).