Skip to content

Configuration Management with pyrs-yaml

Configuration Management with pyrs-yaml

This tutorial walks through a realistic scenario: managing a YAML configuration file for a microservice application. You'll learn how to parse, inspect, edit, validate, and write back a YAML file — all while preserving every comment, anchor, tag, and formatting choice.

Setup

Install from PyPI
pip install pyrs-yaml

1. The Configuration File

We start with a YAML configuration file that has comments, anchors, merge keys, and a mix of block and flow formatting:

config.yaml
# Application configuration (v2.0)
app:
  name: my-service
  version: 2.0

# Default database settings
database: &default-db
  host: localhost
  port: 5432
  name: mydb

# Environment-specific overrides
environments:
  staging:
    <<: *default-db
    host: staging.example.com
    debug: true

  production:
    <<: *default-db
    host: prod.example.com
    port: 5432
    debug: false

# Feature flags
features:
  - name: login
    enabled: true
  - name: export
    enabled: true
  - name: reporting
    enabled: false

# Custom scalar example
threshold: 0x1F  # hex value (should be parsed as int)

2. Parse the File

Parse the file
import pyrs_yaml

doc = pyrs_yaml.parse_file("config.yaml")
print(f"Parsed: {doc.get('app.name')} v{doc.get('app.version')}")
# Parsed: my-service v2.0

Key point: All comments, anchors, tags, and formatting are preserved in memory. The document is a YamlDocument object, not a raw Python dict.

3. Inspect Values

Use the path API (JSONPath-like) or the Node API (tree-based):

Inspect values
# Path API — simple and direct
db_host = doc.get("database.host")
print(f"Database host: {db_host}")

# Node API — access metadata and formatting
db_node = doc.node().find("$.database")
print(f"Database is flow style: {db_node.flow_style}")  # False (block)
print(f"Database anchor: {db_node.anchor}")  # "default-db"

4. Edit Values with Metadata Preservation

When you edit a value, its comment, anchor, tag, and quoting style are preserved. The edit is performed in-place on the AST — no string manipulation:

Edit values in place
# Change the production port
doc.set("$.environments.production.port", 5444)

# Change the app name while keeping its comment
doc.set("$.app.name", "my-service-v2")

# Add a comment to document a change
prod_node = doc.node().find("$.environments.production")
prod_node.set_comment("overridden for v2 rollout")

5. Manipulate Metadata

pyrs-yaml goes beyond value editing — you can read and write the YAML metadata itself:

Read and write metadata
# Read existing metadata
debug_node = doc.node().find("$.environments.staging.debug")
print(f"Debug comment: {debug_node.comment}")  # None

# Add a tag to document a custom type
import_node = doc.node().find("$.threshold")
import_node.set_tag("!!int")
print(f"Threshold tag: {import_node.tag}")  # "!!int"

# Add an anchor for later reference
prod_db = doc.node().find("$.environments.production")
prod_db.set_anchor("prod-db")

6. Control Formatting

Switch scalar quoting, block/flow layout, and chomping indicators:

Control formatting
# Switch the threshold to single-quoted for clarity
doc.node().find("$.threshold").set_scalar_style("single_quoted")

# Switch the staging environment to compact flow style
staging = doc.node().find("$.environments.staging")
staging.set_flow_style(True)

7. Batch Edit with Wildcards

Use set_many to apply changes to every matching path — useful for toggle-like operations:

Batch edit with wildcards
# Disable ALL debug flags across every environment
doc.set_many(
    {
        "$.environments[*].debug": False,
    }
)

# Disable all features at once
doc.set_many(
    {
        "$.features[*].enabled": False,
    }
)

8. Sort Keys

For readability, sort the top-level keys and environment keys:

Sort keys
doc.sort_keys()  # sort the root mapping
doc.sort_keys("$.environments")  # sort the environments

9. Validate Against a Schema

Define a schema with structural rules and validate the configuration:

Validate against a schema
schema = """\
name: app-config
extends: core
validate:
  - path: $.app.name
    type: str
    required: true
  - path: $.environments.*.debug
    type: bool
  - path: $.threshold
    type: int
"""

# Validate — raises YamlValidateError on failure
pyrs_yaml.validate_against_schema(doc.to_yaml(), schema)
print("Configuration is valid!")

10. Deep-Copy a Subtree

Copy a subtree as a standalone Python value (detached from the document):

Deep-copy a subtree
# Copy the staging configuration for reuse
staging_config = doc.node().find("$.environments.staging").copy()
print(staging_config)  # {'host': 'staging.example.com', 'debug': False, ...}

11. Move a Subtree

Relocate a subtree within the same document:

Move a subtree
# Move the reporting feature to a new section
doc.node().find("$.features[2]").move("$.deprecated-features")

12. Write Back to File

Finally, serialize the edited document back to YAML:

Write back to file
output = doc.to_yaml()
with open("config-updated.yaml", "w", encoding="utf-8") as f:
    f.write(output)

The output preserves everything — comments, anchors, merge keys, formatting, and all the edits we made:

config-updated.yaml
# Application configuration (v2.0)
app:
  name: my-service-v2
  version: 2.0

# Default database settings
database: &default-db
  host: localhost
  port: 5432
  name: mydb

# Environment-specific overrides
environments:
  # overridden for v2 rollout
  production: &prod-db
    <<: *default-db
    host: prod.example.com
    port: 5444
    debug: false

  staging:
    <<: *default-db
    debug: false
    host: staging.example.com

Summary

In this tutorial you:

  • Parsed a YAML file with full metadata preservation
  • Inspected values using the path API and Node API
  • Edited values, comments, anchors, tags, and formatting
  • Batch-edited with wildcards using set_many
  • Sorted keys for readability
  • Validated against a schema
  • Copied and moved subtrees
  • Serialized back to YAML with everything preserved

Next Steps