Type stubs for PyYAML, a full-featured YAML framework for Python
Base classes and utilities for creating custom YAML-serializable Python objects with automatic tag registration. These classes enable seamless integration between Python objects and YAML serialization.
Base class for creating Python objects that can be automatically serialized to and from YAML with custom tags.
class YAMLObjectMetaclass(type):
"""
Metaclass for YAML objects that automatically registers constructors and representers.
When a class uses this metaclass, it automatically registers itself
with the specified loaders and dumpers for seamless YAML integration.
"""
class YAMLObject(metaclass=YAMLObjectMetaclass):
"""
Base class for custom YAML-serializable objects.
Subclasses should define class attributes to control YAML behavior:
Class Attributes:
- yaml_loader: Loader class to register constructor with (default: None)
- yaml_dumper: Dumper class to register representer with (default: None)
- yaml_tag: YAML tag for this object type (required)
- yaml_flow_style: Use flow style for serialization (default: None)
"""
yaml_loader: Any = None
yaml_dumper: Any = None
yaml_tag: str = None
yaml_flow_style: bool | None = None
@classmethod
def from_yaml(cls, loader, node):
"""
Construct object from YAML node.
Parameters:
- loader: YAML loader instance
- node: YAML node to construct from
Returns:
- Instance of this class
Override this method to customize object construction from YAML.
"""
@classmethod
def to_yaml(cls, dumper, data):
"""
Represent object as YAML node.
Parameters:
- dumper: YAML dumper instance
- data: Object instance to represent
Returns:
- YAML node representing this object
Override this method to customize object representation to YAML.
"""import yaml
class Person(yaml.YAMLObject):
"""Custom person object with YAML serialization."""
yaml_tag = '!Person'
yaml_loader = yaml.SafeLoader
yaml_dumper = yaml.SafeDumper
def __init__(self, name, age, email=None):
self.name = name
self.age = age
self.email = email
def __repr__(self):
return f"Person(name='{self.name}', age={self.age}, email='{self.email}')"
def __eq__(self, other):
if not isinstance(other, Person):
return False
return (self.name == other.name and
self.age == other.age and
self.email == other.email)
# Create and serialize object
person = Person("John Doe", 30, "john@example.com")
# Dump to YAML
yaml_output = yaml.dump(person, Dumper=yaml.SafeDumper)
print(yaml_output)
# Output:
# !Person
# age: 30
# email: john@example.com
# name: John Doe
# Load from YAML
loaded_person = yaml.load(yaml_output, Loader=yaml.SafeLoader)
print(loaded_person) # Person(name='John Doe', age=30, email='john@example.com')
print(person == loaded_person) # Trueimport yaml
class Point(yaml.YAMLObject):
"""2D point with custom YAML representation."""
yaml_tag = '!Point'
yaml_loader = yaml.SafeLoader
yaml_dumper = yaml.SafeDumper
yaml_flow_style = True # Use flow style: !Point [x, y]
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
@classmethod
def from_yaml(cls, loader, node):
"""Construct Point from YAML sequence [x, y]."""
if isinstance(node, yaml.SequenceNode):
x, y = loader.construct_sequence(node)
return cls(x, y)
elif isinstance(node, yaml.MappingNode):
data = loader.construct_mapping(node)
return cls(data['x'], data['y'])
else:
raise yaml.constructor.ConstructorError(
None, None, f"Expected sequence or mapping, got {node.id}", node.start_mark
)
@classmethod
def to_yaml(cls, dumper, data):
"""Represent Point as YAML sequence [x, y]."""
return dumper.represent_sequence(cls.yaml_tag, [data.x, data.y])
# Test custom Point serialization
points = [Point(1, 2), Point(3.5, -1.0), Point(0, 0)]
yaml_output = yaml.dump(points, Dumper=yaml.SafeDumper)
print(yaml_output)
# Output:
# - !Point [1, 2]
# - !Point [3.5, -1.0]
# - !Point [0, 0]
loaded_points = yaml.load(yaml_output, Loader=yaml.SafeLoader)
print(loaded_points) # [Point(1, 2), Point(3.5, -1.0), Point(0, 0)]import yaml
from datetime import datetime
class Task(yaml.YAMLObject):
"""Task object with complex nested data."""
yaml_tag = '!Task'
yaml_loader = yaml.SafeLoader
yaml_dumper = yaml.SafeDumper
def __init__(self, title, description="", priority="medium",
due_date=None, completed=False, tags=None):
self.title = title
self.description = description
self.priority = priority
self.due_date = due_date
self.completed = completed
self.tags = tags or []
self.created_at = datetime.now()
def __repr__(self):
return f"Task('{self.title}', priority='{self.priority}', completed={self.completed})"
@classmethod
def from_yaml(cls, loader, node):
"""Construct Task from YAML mapping."""
data = loader.construct_mapping(node, deep=True)
# Handle datetime string conversion
if 'due_date' in data and isinstance(data['due_date'], str):
data['due_date'] = datetime.fromisoformat(data['due_date'])
if 'created_at' in data and isinstance(data['created_at'], str):
data['created_at'] = datetime.fromisoformat(data['created_at'])
return cls(**data)
@classmethod
def to_yaml(cls, dumper, data):
"""Represent Task as YAML mapping."""
# Convert datetime objects to ISO strings for YAML compatibility
task_data = {
'title': data.title,
'description': data.description,
'priority': data.priority,
'completed': data.completed,
'tags': data.tags,
'created_at': data.created_at.isoformat()
}
if data.due_date:
task_data['due_date'] = data.due_date.isoformat()
return dumper.represent_mapping(cls.yaml_tag, task_data)
# Create task list
tasks = [
Task("Write documentation", "Complete API documentation", "high",
datetime(2024, 1, 15), tags=["docs", "api"]),
Task("Fix bug #123", "Memory leak in parser", "critical",
datetime(2024, 1, 10), tags=["bug", "parser"]),
Task("Add tests", "Unit tests for new features", "medium",
tags=["tests", "quality"])
]
# Serialize to YAML
yaml_output = yaml.dump(tasks, Dumper=yaml.SafeDumper, default_flow_style=False)
with open('tasks.yaml', 'w') as f:
f.write(yaml_output)
# Load back from YAML
with open('tasks.yaml', 'r') as f:
loaded_tasks = yaml.load(f, Loader=yaml.SafeLoader)
print(f"Loaded {len(loaded_tasks)} tasks")
for task in loaded_tasks:
print(f" {task}")import yaml
class Config(yaml.YAMLObject):
"""Configuration object that works with multiple loaders."""
yaml_tag = '!Config'
# Register with multiple loaders/dumpers
yaml_loader = [yaml.SafeLoader, yaml.FullLoader]
yaml_dumper = [yaml.SafeDumper, yaml.Dumper]
def __init__(self, **kwargs):
self.settings = kwargs
def __getitem__(self, key):
return self.settings[key]
def __setitem__(self, key, value):
self.settings[key] = value
def __repr__(self):
return f"Config({self.settings})"
@classmethod
def from_yaml(cls, loader, node):
"""Construct Config from YAML mapping."""
settings = loader.construct_mapping(node, deep=True)
return cls(**settings)
@classmethod
def to_yaml(cls, dumper, data):
"""Represent Config as YAML mapping."""
return dumper.represent_mapping(cls.yaml_tag, data.settings)
# Usage with different loaders
config_yaml = """
!Config
database:
host: localhost
port: 5432
name: myapp
features:
- authentication
- logging
- metrics
debug: true
"""
# Works with SafeLoader
config1 = yaml.load(config_yaml, Loader=yaml.SafeLoader)
print(f"SafeLoader: {config1}")
# Works with FullLoader
config2 = yaml.load(config_yaml, Loader=yaml.FullLoader)
print(f"FullLoader: {config2}")
# Both produce same result
print(f"Same config: {config1.settings == config2.settings}")import yaml
class Vehicle(yaml.YAMLObject):
"""Base vehicle class."""
yaml_tag = '!Vehicle'
yaml_loader = yaml.SafeLoader
yaml_dumper = yaml.SafeDumper
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def __repr__(self):
return f"{self.__class__.__name__}({self.make} {self.model} {self.year})"
class Car(Vehicle):
"""Car subclass with additional properties."""
yaml_tag = '!Car'
def __init__(self, make, model, year, doors=4):
super().__init__(make, model, year)
self.doors = doors
class Motorcycle(Vehicle):
"""Motorcycle subclass with additional properties."""
yaml_tag = '!Motorcycle'
def __init__(self, make, model, year, engine_cc=None):
super().__init__(make, model, year)
self.engine_cc = engine_cc
# Create vehicle fleet
vehicles = [
Car("Toyota", "Camry", 2023, doors=4),
Motorcycle("Honda", "CBR600RR", 2022, engine_cc=600),
Vehicle("Generic", "Unknown", 2020)
]
# Serialize with specific tags
yaml_output = yaml.dump(vehicles, Dumper=yaml.SafeDumper)
print(yaml_output)
# Output:
# - !Car
# doors: 4
# make: Toyota
# model: Camry
# year: 2023
# - !Motorcycle
# engine_cc: 600
# make: Honda
# model: CBR600RR
# year: 2022
# - !Vehicle
# make: Generic
# model: Unknown
# year: 2020
# Load back with correct types
loaded_vehicles = yaml.load(yaml_output, Loader=yaml.SafeLoader)
for vehicle in loaded_vehicles:
print(f"{type(vehicle).__name__}: {vehicle}")Install with Tessl CLI
npx tessl i tessl/pypi-types-pyyaml