CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-yamale

A schema and validator for YAML with comprehensive data type validation and constraint support.

Overview
Eval results
Files

exceptions.mddocs/

Exception Handling

Error handling and testing utilities for comprehensive validation workflows and error reporting. Yamale provides specialized exception classes and testing utilities for robust error handling in validation scenarios.

Capabilities

YamaleError Exception

Main exception class raised when YAML validation fails, containing detailed error information for all failed validations.

class YamaleError(ValueError):
    """
    Exception raised when YAML validation fails.
    Inherits from ValueError for standard exception handling.
    """
    
    def __init__(self, results):
        """
        Initialize YamaleError with validation results.

        Parameters:
        - results (list): List of ValidationResult objects containing errors

        Attributes:
        - message (str): Combined error message from all failed validations
        - results (list): List of ValidationResult objects
        """

    message: str    # Formatted error message string
    results: list   # List of ValidationResult objects with detailed errors

Usage examples:

import yamale

try:
    yamale.validate(schema, data)
    print("Validation successful!")
except yamale.YamaleError as e:
    # Access the combined error message
    print(f"Validation failed: {e}")
    print(f"Error message: {e.message}")
    
    # Process individual validation results
    print(f"Failed validations: {len([r for r in e.results if not r.isValid()])}")
    for result in e.results:
        if not result.isValid():
            print(f"\nFile: {result.data}")
            print(f"Schema: {result.schema}")
            for error in result.errors:
                print(f"  - {error}")

# Handle as standard ValueError
except ValueError as e:
    print(f"General validation error: {e}")

Detailed Error Information

YamaleError provides structured access to validation failures:

try:
    yamale.validate(schema, data)
except yamale.YamaleError as e:
    # Get summary statistics
    total_results = len(e.results)
    failed_results = [r for r in e.results if not r.isValid()]
    success_rate = (total_results - len(failed_results)) / total_results * 100
    
    print(f"Validation Summary:")
    print(f"  Total files: {total_results}")
    print(f"  Failed: {len(failed_results)}")
    print(f"  Success rate: {success_rate:.1f}%")
    
    # Group errors by type
    error_types = {}
    for result in failed_results:
        for error in result.errors:
            error_type = error.split(':')[0] if ':' in error else 'General'
            error_types[error_type] = error_types.get(error_type, 0) + 1
    
    print(f"\nError breakdown:")
    for error_type, count in error_types.items():
        print(f"  {error_type}: {count}")

YamaleTestCase Class

Unittest.TestCase subclass for easy integration of YAML validation into unit testing frameworks.

class YamaleTestCase(TestCase):
    """
    TestCase subclass for validating YAML files in unit tests.
    Inherits from unittest.TestCase.
    """
    
    schema = None      # String path to schema file (required)
    yaml = None        # String path or list of paths to YAML files (required)  
    base_dir = None    # String path to prepend to all other paths (optional)
    
    def validate(self, validators=None):
        """
        Validate configured YAML files against schema.

        Parameters:
        - validators (dict, optional): Custom validator dictionary

        Returns:
        bool: True if all validations pass

        Raises:
        ValueError: If validation fails or configuration is invalid
        """

Usage examples:

import os
import yamale
from yamale import YamaleTestCase

class TestUserConfigs(YamaleTestCase):
    # Required configuration
    schema = 'schemas/user.yaml'
    yaml = 'data/users/*.yaml'  # Supports glob patterns
    base_dir = os.path.dirname(os.path.realpath(__file__))
    
    def runTest(self):
        # Validate all matching files
        self.assertTrue(self.validate())

class TestMultipleFiles(YamaleTestCase):
    schema = 'config-schema.yaml'
    yaml = ['config.yaml', 'config-prod.yaml', 'config-dev.yaml']
    
    def runTest(self):
        self.assertTrue(self.validate())

class TestWithCustomValidators(YamaleTestCase):
    schema = 'custom-schema.yaml'
    yaml = 'custom-data.yaml'
    
    def runTest(self):
        # Use custom validators
        custom_validators = yamale.validators.DefaultValidators.copy()
        custom_validators['email'] = EmailValidator
        self.assertTrue(self.validate(validators=custom_validators))

# Run tests
if __name__ == '__main__':
    import unittest
    unittest.main()

Advanced Testing Patterns

Test Suite Organization

import unittest
import os
from yamale import YamaleTestCase

class BaseYamaleTest(YamaleTestCase):
    """Base class with common configuration."""
    base_dir = os.path.join(os.path.dirname(__file__), 'fixtures')

class TestAPISchemas(BaseYamaleTest):
    schema = 'api-schema.yaml'
    yaml = 'api-examples/*.yaml'
    
    def runTest(self):
        self.assertTrue(self.validate())

class TestConfigSchemas(BaseYamaleTest):
    schema = 'config-schema.yaml'
    yaml = ['config-*.yaml']
    
    def runTest(self):
        self.assertTrue(self.validate())

# Create test suite
def create_yamale_suite():
    suite = unittest.TestSuite()
    suite.addTest(TestAPISchemas())
    suite.addTest(TestConfigSchemas())
    return suite

Error Handling in Tests

class TestWithErrorHandling(YamaleTestCase):
    schema = 'strict-schema.yaml'
    yaml = 'test-data.yaml'
    
    def runTest(self):
        try:
            result = self.validate()
            self.assertTrue(result)
        except ValueError as e:
            # Log detailed error information for debugging
            self.fail(f"Validation failed: {e}")
    
    def test_expected_failure(self):
        """Test that intentionally invalid data fails validation."""
        # Temporarily change to invalid data
        original_yaml = self.yaml
        self.yaml = 'invalid-data.yaml'
        
        with self.assertRaises(ValueError):
            self.validate()
        
        # Restore original configuration
        self.yaml = original_yaml

Integration with Testing Frameworks

pytest Integration

import pytest
import yamale

def test_yaml_validation():
    """Test YAML validation using pytest."""
    schema = yamale.make_schema('./schema.yaml')
    data = yamale.make_data('./data.yaml')
    
    try:
        yamale.validate(schema, data)
    except yamale.YamaleError as e:
        pytest.fail(f"YAML validation failed: {e}")

@pytest.mark.parametrize("data_file", [
    "config-dev.yaml",
    "config-prod.yaml", 
    "config-test.yaml"
])
def test_multiple_configs(data_file):
    """Test multiple configuration files."""
    schema = yamale.make_schema('./config-schema.yaml')
    data = yamale.make_data(f'./configs/{data_file}')
    
    yamale.validate(schema, data)  # Will raise on failure

def test_validation_with_custom_message():
    """Test with custom error handling."""
    schema = yamale.make_schema('./user-schema.yaml')
    data = yamale.make_data('./invalid-user.yaml')
    
    with pytest.raises(yamale.YamaleError) as exc_info:
        yamale.validate(schema, data)
    
    # Assert specific error conditions
    error_msg = str(exc_info.value)
    assert "age" in error_msg
    assert "required" in error_msg

Error Handling Best Practices

Production Error Handling

import logging
import yamale

def validate_config_file(config_path, schema_path):
    """
    Validate configuration file with comprehensive error handling.
    """
    logger = logging.getLogger(__name__)
    
    try:
        # Create schema
        schema = yamale.make_schema(schema_path)
        logger.info(f"Loaded schema from {schema_path}")
        
        # Load data
        data = yamale.make_data(config_path)
        logger.info(f"Loaded data from {config_path}")
        
        # Validate
        yamale.validate(schema, data)
        logger.info("Validation successful")
        return True
        
    except FileNotFoundError as e:
        logger.error(f"File not found: {e}")
        return False
    except yaml.YAMLError as e:
        logger.error(f"YAML parsing error: {e}")
        return False
    except yamale.YamaleError as e:
        logger.error(f"Validation failed: {e}")
        # Log detailed errors for debugging
        for result in e.results:
            if not result.isValid():
                logger.debug(f"Failed file: {result.data}")
                for error in result.errors:
                    logger.debug(f"  Error: {error}")
        return False
    except Exception as e:
        logger.error(f"Unexpected error: {e}")
        return False

# Usage
if validate_config_file('config.yaml', 'schema.yaml'):
    print("Configuration is valid")
else:
    print("Configuration validation failed")
    sys.exit(1)

Batch Validation with Error Aggregation

def validate_multiple_files(file_paths, schema_path):
    """
    Validate multiple files and aggregate errors.
    """
    schema = yamale.make_schema(schema_path)
    all_results = []
    
    for file_path in file_paths:
        try:
            data = yamale.make_data(file_path)
            results = yamale.validate(schema, data, _raise_error=False)
            all_results.extend(results)
        except Exception as e:
            # Create error result for files that couldn't be loaded
            error_result = yamale.schema.validationresults.Result([str(e)])
            error_result.data = file_path
            error_result.schema = schema_path
            all_results.append(error_result)
    
    # Analyze results
    valid_count = sum(1 for r in all_results if r.isValid())
    total_count = len(all_results)
    
    print(f"Validation Summary: {valid_count}/{total_count} files valid")
    
    # Report failures
    for result in all_results:
        if not result.isValid():
            print(f"FAILED: {result.data}")
            for error in result.errors:
                print(f"  {error}")
    
    return valid_count == total_count

Install with Tessl CLI

npx tessl i tessl/pypi-yamale

docs

cli.md

core-functions.md

exceptions.md

index.md

schema-management.md

validators.md

tile.json