CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-types-pyyaml

Type stubs for PyYAML, a full-featured YAML framework for Python

Overview
Eval results
Files

custom-objects.mddocs/

Custom YAML Objects

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.

Capabilities

YAMLObject Base Class

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.
        """

Usage Examples

Basic Custom Object

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)  # True

Custom Constructor and Representer

import 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)]

Complex Custom Object

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}")

Multiple Loaders and Dumpers

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}")

Inheritance with YAML Objects

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

docs

advanced-components.md

c-extensions.md

custom-objects.md

dumping.md

errors.md

index.md

loaders-dumpers.md

loading.md

low-level.md

registration.md

tile.json