Skip to content

Error Handling in pydapter

pydapter provides a comprehensive error handling system to help you diagnose and resolve issues when working with adapters. This document explains the exception hierarchy and how to handle errors effectively in your applications.

Exception Hierarchy

All pydapter exceptions inherit from the base AdapterError class, which provides context-rich error messages and a consistent interface for error handling.

AdapterError
├── ValidationError
├── ParseError
├── ConnectionError
├── QueryError
├── ResourceError
├── ConfigurationError
└── AdapterNotFoundError

AdapterError

The base exception class for all pydapter errors. It provides a mechanism to attach context information to errors.

try:
    # Some adapter operation
except AdapterError as e:
    print(f"Error message: {e.message}")
    print(f"Error context: {e.context}")

ValidationError

Raised when data validation fails, such as when required fields are missing or have incorrect types.

try:
    model = MyModel.adapt_from(invalid_data, obj_key="json")
except ValidationError as e:
    print(f"Validation failed: {e}")
    print(f"Invalid data: {e.data}")

ParseError

Raised when data parsing fails, such as when trying to parse invalid JSON, CSV, or TOML.

try:
    model = MyModel.adapt_from('{"invalid": json', obj_key="json")
except ParseError as e:
    print(f"Parse error: {e}")
    print(f"Source: {e.source}")

ConnectionError

Raised when a connection to a data source fails, such as when a database is unavailable.

try:
    model = MyModel.adapt_from({"engine_url": "invalid://url", "table": "test"}, obj_key="sql")
except ConnectionError as e:
    print(f"Connection failed: {e}")
    print(f"Adapter: {e.adapter}")
    print(f"URL: {e.url}")

QueryError

Raised when a query to a data source fails, such as when an SQL query contains errors.

try:
    model = MyModel.adapt_from({"engine_url": "sqlite://", "table": "test", "query": "INVALID SQL"}, obj_key="sql")
except QueryError as e:
    print(f"Query failed: {e}")
    print(f"Query: {e.query}")
    print(f"Adapter: {e.adapter}")

ResourceError

Raised when a resource (file, database table, etc.) cannot be accessed.

try:
    model = MyModel.adapt_from(Path("nonexistent.json"), obj_key="json")
except ResourceError as e:
    print(f"Resource error: {e}")
    print(f"Resource: {e.resource}")

ConfigurationError

Raised when adapter configuration is invalid, such as when required parameters are missing.

try:
    model = MyModel.adapt_from({"missing": "required_params"}, obj_key="sql")
except ConfigurationError as e:
    print(f"Configuration error: {e}")
    print(f"Config: {e.config}")

AdapterNotFoundError

Raised when an adapter is not found for a given obj_key.

try:
    model = MyModel.adapt_from({}, obj_key="nonexistent")
except AdapterNotFoundError as e:
    print(f"Adapter not found: {e}")
    print(f"Object key: {e.obj_key}")

Error Handling Best Practices

Catch Specific Exceptions

Catch the most specific exception type that applies to your situation:

try:
    model = MyModel.adapt_from(data, obj_key="json")
except ParseError:
    # Handle parsing errors
except ValidationError:
    # Handle validation errors
except AdapterError:
    # Handle any other adapter errors

Provide Context in Error Messages

When raising custom exceptions, provide as much context as possible:

raise ConnectionError(
    "Failed to connect to database",
    adapter="postgres",
    url="postgresql://localhost:5432/mydb",
    timeout=30
)

Handle Asynchronous Errors

For asynchronous adapters, use try/except blocks within async functions:

async def fetch_data():
    try:
        return await MyModel.adapt_from_async(data, obj_key="async_mongo")
    except ConnectionError as e:
        logger.error(f"Connection failed: {e}")
        # Handle connection error
    except AdapterError as e:
        logger.error(f"Adapter error: {e}")
        # Handle other adapter errors

Resource Cleanup

Ensure resources are properly cleaned up, even in error scenarios:

try:
    # Some operation that acquires resources
    result = perform_operation()
    return result
except AdapterError:
    # Handle the error
    raise
finally:
    # Clean up resources
    cleanup_resources()

Common Error Scenarios and Solutions

JSON Parsing Errors

try:
    model = MyModel.adapt_from(json_data, obj_key="json")
except ParseError as e:
    if "Expecting property name" in str(e):
        # Handle malformed JSON
    elif "Expecting value" in str(e):
        # Handle empty JSON

Database Connection Errors

try:
    model = MyModel.adapt_from(db_config, obj_key="postgres")
except ConnectionError as e:
    if "authentication failed" in str(e):
        # Handle authentication issues
    elif "connection refused" in str(e):
        # Handle server unavailable
    elif "database does not exist" in str(e):
        # Handle missing database

Empty Result Sets

try:
    model = MyModel.adapt_from(query_params, obj_key="mongo", many=False)
except ResourceError as e:
    if "No documents found" in str(e):
        # Handle empty result
        return default_value

Conclusion

Proper error handling is essential for building robust applications with pydapter. By understanding the exception hierarchy and following best practices, you can create more resilient code that gracefully handles failure scenarios and provides clear feedback to users.