Attrs Request/Response¶
Advanced features - Uses the attrs library for more features than standard dataclasses.
Best For¶
- More features than standard dataclasses
- Functional-style codebases
- When you need more control than dataclasses
- Complex validation scenarios
Usage¶
import cqrs
import attrs
from typing import Self
@attrs.define
class AttrsRequest(cqrs.IRequest):
"""Request using attrs."""
user_id: str = attrs.field(validator=attrs.validators.instance_of(str))
action: str = attrs.field(validator=attrs.validators.instance_of(str))
def to_dict(self) -> dict:
"""Convert to dictionary."""
return attrs.asdict(self)
@classmethod
def from_dict(cls, **kwargs) -> Self:
"""Create from dictionary."""
return cls(**kwargs)
@attrs.define(frozen=True)
class AttrsResponse(cqrs.IResponse):
"""Immutable response using attrs."""
result: str = attrs.field()
status: str = attrs.field()
def to_dict(self) -> dict:
"""Convert to dictionary."""
return attrs.asdict(self)
@classmethod
def from_dict(cls, **kwargs) -> Self:
"""Create from dictionary."""
return cls(**kwargs)
class AttrsHandler(cqrs.RequestHandler[AttrsRequest, AttrsResponse]):
@property
def events(self) -> list[cqrs.IEvent]:
return []
async def handle(self, request: AttrsRequest) -> AttrsResponse:
return AttrsResponse(
result=f"Processed {request.action}",
status="success"
)
Validation¶
Attrs provides powerful validation capabilities:
import attrs
from typing import Self
@attrs.define
class ValidatedRequest(cqrs.IRequest):
email: str = attrs.field(
validator=attrs.validators.matches_re(r'^[\w\.-]+@[\w\.-]+\.\w+$')
)
age: int = attrs.field(
validator=attrs.validators.and_(
attrs.validators.ge(0),
attrs.validators.le(120)
)
)
def to_dict(self) -> dict:
return attrs.asdict(self)
@classmethod
def from_dict(cls, **kwargs) -> Self:
return cls(**kwargs)
Custom Converters¶
You can use converters for automatic type conversion:
@attrs.define
class ConvertedRequest(cqrs.IRequest):
count: int = attrs.field(converter=int)
tags: list[str] = attrs.field(
converter=lambda x: x.split(",") if isinstance(x, str) else x
)
def to_dict(self) -> dict:
return attrs.asdict(self)
@classmethod
def from_dict(cls, **kwargs) -> Self:
return cls(**kwargs)
Factory Functions¶
Attrs supports factory functions for default values:
@attrs.define
class RequestWithDefaults(cqrs.IRequest):
user_id: str
metadata: dict = attrs.field(factory=dict)
tags: list[str] = attrs.field(factory=list)
def to_dict(self) -> dict:
return attrs.asdict(self)
@classmethod
def from_dict(cls, **kwargs) -> Self:
return cls(**kwargs)
Immutable Classes¶
Use frozen=True for immutability:
@attrs.define(frozen=True)
class ImmutableRequest(cqrs.IRequest):
user_id: str
action: str
def to_dict(self) -> dict:
return attrs.asdict(self)
@classmethod
def from_dict(cls, **kwargs) -> Self:
return cls(**kwargs)
See Also¶
- Dataclasses - Standard library alternative
- Request Handlers - Learn about handler implementation