w3resource

Python Data Validation Library using Dataclasses and type hints

Write a Python program to create a data validation library using Python's dataclasses and type hints.

The task is to develop a Python program that creates a data validation library utilizing Python's "dataclasses" and type hints. This library enables automatic validation of "dataclass" fields based on specified type hints and custom validation functions. By integrating these features, the program will facilitate robust data validation, ensuring data conforms to expected types and constraints. This will enhance data integrity and reducing runtime errors in applications.

Sample Solution:

Python Code :

# Import necessary modules
from dataclasses import dataclass, field, fields, Field
from typing import Any, Callable, List, get_type_hints

# Custom exception for validation errors
class ValidationError(Exception):
    pass

# Function to validate dataclass fields
def validate(instance):
    cls = instance.__class__
    hints = get_type_hints(cls)
    # Iterate through fields and type hints
    for name, type_hint in hints.items():
        value = getattr(instance, name)
        # Check if value matches type hint
        if not isinstance(value, type_hint):
            raise ValidationError(f"Field '{name}' expects {type_hint}, got {type(value)}")
        # Check for custom validators
        field: Field = next(f for f in fields(cls) if f.name == name)
        if 'validators' in field.metadata:
            for validator in field.metadata['validators']:
                validator(instance, value)

# Decorator to define a validator function
def validator(func: Callable[[Any, Any], None]):
    if not callable(func):
        raise ValueError("Validator must be callable")
    func._is_validator = True
    return func

# Function to define a field with validation
def field_with_validation(*, default: Any = None, default_factory: Callable[[], Any] = None, validators: List[Callable[[Any, Any], None]] = None) -> Field:
    # Ensure only one of default or default_factory is provided
    if default is not None and default_factory is not None:
        raise ValueError('cannot specify both default and default_factory')
    
    metadata = {}
    # Attach validators to metadata
    if validators:
        metadata['validators'] = validators
    
    # Define field with appropriate parameters
    if default is not None:
        return field(default=default, metadata=metadata)
    elif default_factory is not None:
        return field(default_factory=default_factory, metadata=metadata)
    else:
        return field(metadata=metadata)

# Dataclass representing a User with validation
@dataclass
class User:
    # Define fields with validation
    name: str
    age: int = field_with_validation(default=0, validators=[
        validator(lambda instance, value: value >= 0 or ValidationError("Age must be non-negative")),
        validator(lambda instance, value: value <= 150 or ValidationError("Age must be 150 or less"))
    ])
    email: str = field_with_validation(default="", validators=[
        validator(lambda instance, value: "@" in value or ValidationError("Invalid email address"))
    ])

# Example usage
try:
    user = User(name="Arsen", age=30, email="arsen@example.com")
    validate(user)
    print("User is valid")
except ValidationError as e:
    print(f"Validation error: {e}")

Output:

User is valid

Explanation:

  • Imports:
    The "dataclasses", "Callable", "List", and "get_type_hints" modules are imported to support dataclass creation and type hinting.
  • Custom exception:
    The ValidationError class is defined to handle validation errors.
  • Validation Function (validate):
    This function takes an instance of a dataclass and validates its fields against their type hints and any registered validators.
  • Decorator for Validators (validator):
    This decorator is used to define custom validation functions. It checks if the provided validator is callable and marks it as a validator.
  • Function to define fields with validation (field_with_validation):
    This function creates 'dataclass' fields with validation metadata attached.
  • It allows specifying default values, default factories, and a list of validators for each field.
  • Dataclass Definition (User):
    A "User" dataclass is defined with fields for 'name', 'age', and 'email', each with validation applied using the "field_with_validation" function.
  • Example usage:
    An example 'User' instance is created with validated fields ('name', 'age', and 'email').
  • The "validate" function is called to validate the 'User' instance.
  • If validation fails, a 'ValidationError' is raised and caught, and an appropriate error message is printed.

Python Code Editor :

Have another way to solve this solution? Contribute your code (and comments) through Disqus.

Previous: Python Thread-Safe Priority Queue implementation.
Next: Python A Search Algorithm for Pathfinding.

What is the difficulty level of this exercise?

Test your Programming skills with w3resource's quiz.



Become a Patron!

Follow us on Facebook and Twitter for latest update.

It will be nice if you may share this link in any developer community or anywhere else, from where other developers may find this content. Thanks.

https://198.211.115.131/python-exercises/advanced/python-data-validation-library-with-dataclasses-and-type-hints.php