CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-json-rpc

JSON-RPC transport implementation for Python supporting both 1.0 and 2.0 protocols with Django and Flask backends

Pending
Overview
Eval results
Files

exceptions.mddocs/

Exceptions and Error Handling

Comprehensive error handling system with predefined JSON-RPC error types, custom exception support, and automatic error response generation. The library provides both JSON-RPC standard errors and Python exception integration.

Capabilities

JSON-RPC Error Objects

Core error object representing JSON-RPC errors with code, message, and optional data fields.

class JSONRPCError:
    serialize = staticmethod  # json.dumps
    deserialize = staticmethod  # json.loads
    
    def __init__(self, code: int = None, message: str = None, data = None):
        """
        Create JSON-RPC error object.
        
        Parameters:
        - code: Error code (integer, -32768 to -32000 reserved)
        - message: Brief error description (string)
        - data: Additional error information (any type)
        """
    
    @classmethod
    def from_json(cls, json_str: str):
        """Create error from JSON string."""
    
    # Properties
    code: int
    message: str
    data  # Any type
    json: str  # JSON representation

Predefined Error Types

Standard JSON-RPC error types with predefined codes and messages.

class JSONRPCParseError(JSONRPCError):
    """Invalid JSON was received by the server."""
    CODE = -32700
    MESSAGE = "Parse error"

class JSONRPCInvalidRequest(JSONRPCError):
    """The JSON sent is not a valid Request object."""
    CODE = -32600
    MESSAGE = "Invalid Request"

class JSONRPCMethodNotFound(JSONRPCError):
    """The method does not exist / is not available."""
    CODE = -32601
    MESSAGE = "Method not found"

class JSONRPCInvalidParams(JSONRPCError):
    """Invalid method parameter(s)."""
    CODE = -32602
    MESSAGE = "Invalid params"

class JSONRPCInternalError(JSONRPCError):
    """Internal JSON-RPC error."""
    CODE = -32603
    MESSAGE = "Internal error"

class JSONRPCServerError(JSONRPCError):
    """Reserved for implementation-defined server-errors."""
    CODE = -32000
    MESSAGE = "Server error"

Python Exception Classes

Standard Python exceptions for different error conditions.

class JSONRPCException(Exception):
    """Base JSON-RPC exception."""
    pass

class JSONRPCInvalidRequestException(JSONRPCException):
    """Request is not valid."""
    pass

class JSONRPCDispatchException(JSONRPCException):
    """
    JSON-RPC dispatch exception for method implementations.
    
    Should be thrown in dispatch methods to return custom errors.
    """
    
    def __init__(self, code: int = None, message: str = None, data = None, *args, **kwargs):
        """
        Create dispatch exception with error details.
        
        Parameters:
        - code: JSON-RPC error code
        - message: Error message
        - data: Additional error data
        - args, kwargs: Standard exception arguments
        """
    
    error: JSONRPCError  # Associated error object

Error Detection Utilities

Utility functions for detecting and validating parameter errors.

def is_invalid_params(func, *args, **kwargs) -> bool:
    """
    Check if function parameters are invalid.
    
    Used internally to distinguish TypeError from invalid parameters
    vs TypeError from within the function.
    
    Parameters:
    - func: Function to validate against
    - args: Positional arguments
    - kwargs: Keyword arguments
    
    Returns:
    True if parameters are invalid for the function
    """

Usage Examples

Basic Error Handling

from jsonrpc import dispatcher, JSONRPCResponseManager
from jsonrpc.exceptions import JSONRPCDispatchException

@dispatcher.add_method
def divide(a, b):
    if b == 0:
        raise JSONRPCDispatchException(
            code=-32602,
            message="Division by zero",
            data={"dividend": a, "divisor": b}
        )
    return a / b

@dispatcher.add_method
def validate_age(age):
    if not isinstance(age, int) or age < 0:
        raise JSONRPCDispatchException(
            code=-32602,
            message="Invalid age parameter",
            data={"received": age, "expected": "positive integer"}
        )
    return {"valid": True, "age": age}

# Error response
request = '{"jsonrpc": "2.0", "method": "divide", "params": [10, 0], "id": 1}'
response = JSONRPCResponseManager.handle(request, dispatcher)
print(response.json)
# {
#   "jsonrpc": "2.0",
#   "error": {
#     "code": -32602,
#     "message": "Division by zero", 
#     "data": {"dividend": 10, "divisor": 0}
#   },
#   "id": 1
# }

Predefined Error Types

from jsonrpc.exceptions import (
    JSONRPCMethodNotFound, 
    JSONRPCInvalidParams,
    JSONRPCInternalError
)

# Method not found (automatically handled by manager)
request = '{"jsonrpc": "2.0", "method": "nonexistent", "id": 1}'
response = JSONRPCResponseManager.handle(request, dispatcher)
print(response.json)
# {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": 1}

# Invalid parameters (automatically detected)
@dispatcher.add_method
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

# Missing required parameter
request = '{"jsonrpc": "2.0", "method": "greet", "params": [], "id": 1}'
response = JSONRPCResponseManager.handle(request, dispatcher)
print(response.json)
# {"jsonrpc": "2.0", "error": {"code": -32602, "message": "Invalid params", "data": {...}}, "id": 1}

# Too many parameters
request = '{"jsonrpc": "2.0", "method": "greet", "params": ["Alice", "Hi", "Extra"], "id": 1}'
response = JSONRPCResponseManager.handle(request, dispatcher)
print(response.json)
# {"jsonrpc": "2.0", "error": {"code": -32602, "message": "Invalid params", "data": {...}}, "id": 1}

Custom Error Creation

from jsonrpc.exceptions import JSONRPCError

# Create custom error
custom_error = JSONRPCError(
    code=-32001,
    message="Database connection failed",
    data={
        "database": "users",
        "host": "localhost:5432",
        "timestamp": "2023-01-01T12:00:00Z"
    }
)

print(custom_error.json)
# {
#   "code": -32001,
#   "message": "Database connection failed",
#   "data": {
#     "database": "users",
#     "host": "localhost:5432", 
#     "timestamp": "2023-01-01T12:00:00Z"
#   }
# }

# Use in dispatch exception
@dispatcher.add_method
def get_user(user_id):
    try:
        # Simulate database operation
        if user_id == 999:
            raise ConnectionError("Database unavailable")
        return {"id": user_id, "name": f"User {user_id}"}
    except ConnectionError as e:
        raise JSONRPCDispatchException(
            code=-32001,
            message="Database connection failed",
            data={"error": str(e), "user_id": user_id}
        )

Error Response Structure

from jsonrpc.exceptions import JSONRPCError
from jsonrpc.jsonrpc2 import JSONRPC20Response

# Manual error response creation
error = JSONRPCError(
    code=-32603,
    message="Internal error",
    data={"component": "user_service", "error_id": "USR_001"}
)

response = JSONRPC20Response(error=error._data, _id=123)
print(response.json)
# {
#   "jsonrpc": "2.0",
#   "error": {
#     "code": -32603,
#     "message": "Internal error",
#     "data": {"component": "user_service", "error_id": "USR_001"}
#   },
#   "id": 123
# }

Parse and Request Errors

from jsonrpc import JSONRPCResponseManager

# Parse error (invalid JSON)
invalid_json = '{"jsonrpc": "2.0", "method": "test", invalid}'
response = JSONRPCResponseManager.handle(invalid_json, dispatcher)
print(response.json)
# {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}

# Invalid request structure
invalid_request = '{"jsonrpc": "2.0", "params": [1, 2], "id": 1}'  # Missing method
response = JSONRPCResponseManager.handle(invalid_request, dispatcher)
print(response.json)  
# {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}

# Batch with invalid request
batch_with_error = '''[
    {"jsonrpc": "2.0", "method": "add", "params": [1, 2], "id": "1"},
    {"jsonrpc": "2.0", "params": [3, 4], "id": "2"},
    {"jsonrpc": "2.0", "method": "multiply", "params": [5, 6], "id": "3"}
]'''
response = JSONRPCResponseManager.handle(batch_with_error, dispatcher)
# Will return error for the invalid request in the batch

Exception Handling in Methods

from jsonrpc.exceptions import JSONRPCDispatchException
import logging

logger = logging.getLogger(__name__)

@dispatcher.add_method
def process_file(filename):
    try:
        # Simulate file processing
        if not filename:
            raise ValueError("Filename cannot be empty")
        
        if not filename.endswith('.txt'):
            raise JSONRPCDispatchException(
                code=-32602,
                message="Invalid file type",
                data={"filename": filename, "supported": [".txt"]}
            )
        
        # Simulate processing
        return {"status": "processed", "filename": filename}
        
    except ValueError as e:
        logger.error(f"Validation error: {e}")
        raise JSONRPCDispatchException(
            code=-32602,
            message="Validation failed",
            data={"error": str(e)}
        )
    except Exception as e:
        logger.exception("Unexpected error processing file")
        raise JSONRPCDispatchException(
            code=-32603,
            message="Internal processing error",
            data={"filename": filename, "error_type": type(e).__name__}
        )

# Test error cases
request1 = '{"jsonrpc": "2.0", "method": "process_file", "params": [""], "id": 1}'
response1 = JSONRPCResponseManager.handle(request1, dispatcher)
# Returns validation error

request2 = '{"jsonrpc": "2.0", "method": "process_file", "params": ["doc.pdf"], "id": 2}'
response2 = JSONRPCResponseManager.handle(request2, dispatcher)
# Returns invalid file type error

Error Code Conventions

# Standard JSON-RPC error codes
PARSE_ERROR = -32700        # Invalid JSON
INVALID_REQUEST = -32600    # Invalid request object
METHOD_NOT_FOUND = -32601   # Method doesn't exist
INVALID_PARAMS = -32602     # Invalid parameters
INTERNAL_ERROR = -32603     # Internal JSON-RPC error

# Server error range: -32000 to -32099 (reserved for implementation)
DATABASE_ERROR = -32001
AUTHENTICATION_ERROR = -32002
AUTHORIZATION_ERROR = -32003
RATE_LIMIT_ERROR = -32004
VALIDATION_ERROR = -32005

# Application-specific errors: -32100 and below
USER_NOT_FOUND = -32100
INSUFFICIENT_FUNDS = -32101
PRODUCT_OUT_OF_STOCK = -32102

@dispatcher.add_method
def transfer_funds(from_account, to_account, amount):
    if amount <= 0:
        raise JSONRPCDispatchException(
            code=VALIDATION_ERROR,
            message="Invalid transfer amount",
            data={"amount": amount}
        )
    
    # Check balance
    if get_balance(from_account) < amount:
        raise JSONRPCDispatchException(
            code=INSUFFICIENT_FUNDS,
            message="Insufficient funds for transfer",
            data={
                "from_account": from_account,
                "requested": amount,
                "available": get_balance(from_account)
            }
        )
    
    # Perform transfer
    return {"status": "success", "transaction_id": "TXN123"}

Install with Tessl CLI

npx tessl i tessl/pypi-json-rpc

docs

backends.md

core-jsonrpc.md

dispatcher.md

exceptions.md

index.md

requests-responses.md

tile.json