Type stubs for PyYAML, a full-featured YAML framework for Python
Functions for registering custom constructors, representers, and resolvers to extend YAML processing capabilities. These functions allow you to customize how PyYAML handles specific data types and tags.
Functions for registering custom constructors that convert YAML nodes to Python objects.
def add_constructor(tag: str, constructor: Callable, Loader=None) -> None:
"""
Register a constructor for a specific YAML tag.
Parameters:
- tag: YAML tag to handle (e.g., '!custom', 'tag:yaml.org,2002:python/tuple')
- constructor: Function that takes (loader, node) and returns Python object
- Loader: Loader class to register with (defaults to global registration)
The constructor function signature:
def constructor(loader, node) -> Any
"""
def add_multi_constructor(tag_prefix: str, multi_constructor: Callable, Loader=None) -> None:
"""
Register a multi-constructor for YAML tags with a common prefix.
Parameters:
- tag_prefix: YAML tag prefix to handle (e.g., '!python/', 'tag:example.com,2002:')
- multi_constructor: Function that takes (loader, tag_suffix, node) and returns Python object
- Loader: Loader class to register with (defaults to global registration)
The multi-constructor function signature:
def multi_constructor(loader, tag_suffix, node) -> Any
"""Functions for registering custom representers that convert Python objects to YAML nodes.
def add_representer(data_type: type, representer: Callable, Dumper=None) -> None:
"""
Register a representer for a specific Python type.
Parameters:
- data_type: Python type to handle (e.g., MyClass, datetime.date)
- representer: Function that takes (dumper, data) and returns YAML node
- Dumper: Dumper class to register with (defaults to global registration)
The representer function signature:
def representer(dumper, data) -> Node
"""
def add_multi_representer(data_type: type, multi_representer: Callable, Dumper=None) -> None:
"""
Register a multi-representer for a type and its subclasses.
Parameters:
- data_type: Base Python type to handle (will also handle subclasses)
- multi_representer: Function that takes (dumper, data) and returns YAML node
- Dumper: Dumper class to register with (defaults to global registration)
The multi-representer function signature:
def multi_representer(dumper, data) -> Node
"""Functions for registering custom resolvers that determine YAML tags for values.
def add_implicit_resolver(tag: str, regexp: Pattern[str], first=None, Loader=None, Dumper=None) -> None:
"""
Register an implicit resolver that assigns tags based on value patterns.
Parameters:
- tag: YAML tag to assign when pattern matches
- regexp: Compiled regular expression pattern to match against scalar values
- first: Set of possible first characters (optimization hint)
- Loader: Loader class to register with (defaults to global registration)
- Dumper: Dumper class to register with (defaults to global registration)
"""
def add_path_resolver(tag: str, path: Iterable[Any], kind=None, Loader=None, Dumper=None) -> None:
"""
Register a path resolver that assigns tags based on document path.
Parameters:
- tag: YAML tag to assign when path matches
- path: Sequence describing the path in the document structure
- kind: Node kind to match (ScalarNode, SequenceNode, MappingNode)
- Loader: Loader class to register with (defaults to global registration)
- Dumper: Dumper class to register with (defaults to global registration)
"""import yaml
import re
from datetime import datetime
# Constructor for custom date format
def date_constructor(loader, node):
"""Construct datetime from custom date format."""
value = loader.construct_scalar(node)
return datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
# Register constructor for custom tag
yaml.add_constructor('!datetime', date_constructor, Loader=yaml.SafeLoader)
# Test custom constructor
yaml_input = """
created: !datetime 2024-01-15 14:30:00
modified: !datetime 2024-01-16 09:15:30
"""
data = yaml.load(yaml_input, Loader=yaml.SafeLoader)
print(f"Created: {data['created']} (type: {type(data['created'])})")
print(f"Modified: {data['modified']} (type: {type(data['modified'])})")import yaml
import importlib
def python_object_constructor(loader, tag_suffix, node):
"""Construct Python objects from module.class notation."""
if tag_suffix == 'object':
# Handle !!python/object:module.Class
class_name = loader.construct_scalar(node)
module_name, class_name = class_name.rsplit('.', 1)
module = importlib.import_module(module_name)
cls = getattr(module, class_name)
return cls()
elif tag_suffix == 'apply':
# Handle !!python/apply:function [args]
function_name = node.value[0].value
args = loader.construct_sequence(node.value[1])
module_name, func_name = function_name.rsplit('.', 1)
module = importlib.import_module(module_name)
func = getattr(module, func_name)
return func(*args)
else:
raise yaml.constructor.ConstructorError(
None, None, f"Unknown python tag suffix: {tag_suffix}", node.start_mark
)
# Register multi-constructor for python tags
yaml.add_multi_constructor('tag:yaml.org,2002:python/',
python_object_constructor,
Loader=yaml.UnsafeLoader)
# Test (UNSAFE - only for demonstration)
yaml_input = """
date_func: !!python/object:datetime.date
result: !!python/apply:datetime.date [2024, 1, 15]
"""
# This would work with UnsafeLoader (not recommended for untrusted input)
# data = yaml.load(yaml_input, Loader=yaml.UnsafeLoader)import yaml
from decimal import Decimal
def decimal_representer(dumper, data):
"""Represent Decimal as a scalar with custom tag."""
return dumper.represent_scalar('!decimal', str(data))
def decimal_constructor(loader, node):
"""Construct Decimal from scalar value."""
value = loader.construct_scalar(node)
return Decimal(value)
# Register representer and constructor
yaml.add_representer(Decimal, decimal_representer, Dumper=yaml.SafeDumper)
yaml.add_constructor('!decimal', decimal_constructor, Loader=yaml.SafeLoader)
# Test custom Decimal handling
data = {
'price': Decimal('19.99'),
'tax_rate': Decimal('0.0825'),
'total': Decimal('21.64')
}
yaml_output = yaml.dump(data, Dumper=yaml.SafeDumper)
print("YAML output:")
print(yaml_output)
# Output:
# price: !decimal '19.99'
# tax_rate: !decimal '0.0825'
# total: !decimal '21.64'
loaded_data = yaml.load(yaml_output, Loader=yaml.SafeLoader)
print(f"Loaded price: {loaded_data['price']} (type: {type(loaded_data['price'])})")import yaml
from pathlib import Path
def path_representer(dumper, data):
"""Represent Path objects as strings with custom tag."""
return dumper.represent_scalar('!path', str(data))
def path_constructor(loader, node):
"""Construct Path from string value."""
value = loader.construct_scalar(node)
return Path(value)
# Register for Path and all its subclasses
yaml.add_multi_representer(Path, path_representer, Dumper=yaml.SafeDumper)
yaml.add_constructor('!path', path_constructor, Loader=yaml.SafeLoader)
# Test with different Path types
from pathlib import WindowsPath, PosixPath
data = {
'config_file': Path('/etc/myapp/config.yaml'),
'data_dir': Path('/var/lib/myapp'),
'log_file': Path('/var/log/myapp.log')
}
yaml_output = yaml.dump(data, Dumper=yaml.SafeDumper)
print("YAML output:")
print(yaml_output)
loaded_data = yaml.load(yaml_output, Loader=yaml.SafeLoader)
print(f"Config file: {loaded_data['config_file']} (type: {type(loaded_data['config_file'])})")import yaml
import re
from ipaddress import IPv4Address, IPv6Address
# IPv4 address pattern
ipv4_pattern = re.compile(r'^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$')
def ipv4_constructor(loader, node):
value = loader.construct_scalar(node)
return IPv4Address(value)
def ipv4_representer(dumper, data):
return dumper.represent_scalar('!ipv4', str(data))
# Register IPv4 handling
yaml.add_constructor('!ipv4', ipv4_constructor, Loader=yaml.SafeLoader)
yaml.add_representer(IPv4Address, ipv4_representer, Dumper=yaml.SafeDumper)
# Register implicit resolver - automatically detect IPv4 addresses
yaml.add_implicit_resolver('!ipv4', ipv4_pattern, ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
Loader=yaml.SafeLoader, Dumper=yaml.SafeDumper)
# Test automatic IPv4 detection
yaml_input = """
server: 192.168.1.1
database: 10.0.0.5
client: not.an.ip.address
"""
data = yaml.load(yaml_input, Loader=yaml.SafeLoader)
print(f"Server: {data['server']} (type: {type(data['server'])})")
print(f"Database: {data['database']} (type: {type(data['database'])})")
print(f"Client: {data['client']} (type: {type(data['client'])})")
# Dump back to YAML
yaml_output = yaml.dump(data, Dumper=yaml.SafeDumper)
print("\nYAML output:")
print(yaml_output)import yaml
def special_string_constructor(loader, node):
"""Constructor for special strings in specific locations."""
value = loader.construct_scalar(node)
return f"SPECIAL: {value}"
# Register constructor
yaml.add_constructor('!special', special_string_constructor, Loader=yaml.SafeLoader)
# Register path resolver - apply !special tag to strings at specific paths
yaml.add_path_resolver('!special', ['config', 'database', 'password'], str,
Loader=yaml.SafeLoader)
yaml.add_path_resolver('!special', ['secrets', None], str, # Any key under 'secrets'
Loader=yaml.SafeLoader)
# Test path-based tag resolution
yaml_input = """
config:
database:
host: localhost
password: secret123 # Will get !special tag
port: 5432
app:
name: MyApp
debug: true
secrets:
api_key: abc123 # Will get !special tag
token: xyz789 # Will get !special tag
normal_password: plain # Won't get !special tag
"""
data = yaml.load(yaml_input, Loader=yaml.SafeLoader)
print("Loaded data:")
print(f"Database password: {data['config']['database']['password']}")
print(f"API key: {data['secrets']['api_key']}")
print(f"Token: {data['secrets']['token']}")
print(f"Normal password: {data['normal_password']}")import yaml
from typing import NamedTuple
from enum import Enum
class Priority(Enum):
LOW = 1
MEDIUM = 2
HIGH = 3
CRITICAL = 4
class Task(NamedTuple):
title: str
priority: Priority
completed: bool = False
# Priority enum handling
def priority_representer(dumper, data):
return dumper.represent_scalar('!priority', data.name.lower())
def priority_constructor(loader, node):
value = loader.construct_scalar(node)
return Priority[value.upper()]
# Task handling
def task_representer(dumper, data):
return dumper.represent_mapping('!task', {
'title': data.title,
'priority': data.priority,
'completed': data.completed
})
def task_constructor(loader, node):
data = loader.construct_mapping(node)
return Task(**data)
# Register all custom types
yaml.add_representer(Priority, priority_representer, Dumper=yaml.SafeDumper)
yaml.add_constructor('!priority', priority_constructor, Loader=yaml.SafeLoader)
yaml.add_representer(Task, task_representer, Dumper=yaml.SafeDumper)
yaml.add_constructor('!task', task_constructor, Loader=yaml.SafeLoader)
# Test complete type system
tasks = [
Task("Fix critical bug", Priority.CRITICAL, False),
Task("Write documentation", Priority.MEDIUM, True),
Task("Code review", Priority.HIGH, False)
]
yaml_output = yaml.dump(tasks, Dumper=yaml.SafeDumper)
print("YAML output:")
print(yaml_output)
loaded_tasks = yaml.load(yaml_output, Loader=yaml.SafeLoader)
print("Loaded tasks:")
for task in loaded_tasks:
print(f" {task.title}: {task.priority.name} ({'✓' if task.completed else '✗'})")Install with Tessl CLI
npx tessl i tessl/pypi-types-pyyaml