from __future__ import annotations
from datetime import datetime, UTC
from typing import Any, Generic, Self, TypeVar
from pydantic import AwareDatetime, BaseModel
EventDataT_co = TypeVar('EventDataT_co', covariant=True)
class Event(BaseModel, Generic[EventDataT_co]):
raised_at: AwareDatetime
data: tuple[EventDataT_co, ...]
@classmethod
def from_data(cls, *data: EventDataT_co) -> Self:
return cls(raised_at=datetime.now(UTC), data=data)
def save_events(*events: Event): ... # not type-safe (someone might make unsafe assumption on `.data`)
def save_events(*events: Event[Any]): ... # same
def save_events(*events: Event[object]): ... # Only allowed if Event is covariant, but then I can't have a custom constructor
def save_events(*events: Event[EventDataT_co]): ... # It implies this is a generic function, when it's more `.data`-agnostic
Mypy throws error: Cannot use a covariant type variable as a parameter [misc]
on the def from_data(...)
constructor line.
I have no idea how to solve this. I’ll admit, I’m new to the concept of variance, and it’s not clear to me whether I’m doing something wrong (is Event
even covariant to begin with) or if mypy
is not allowing something it should. I’m trying to avoid using Any
here, because save_events
is actually a method of a different class that might be subclassed, and I wouldn’t want the dev that inherits from it to get bitten by my use of Any
. Event[object]
seems like the safest, but it requires covariance. Not only does that mean no custom constructor (I think), it also means actual covariance and I’m not sure my event class is covariant outside of this specific usage with object
(i.e.: I don’t see a use case where a function handle_foo_created(event: Event[FooParent])
should ever accept Event[FooChild]
, nor do I think event: FooParentEvent = FooParentEvent.from_data(FooChild())
is a use-case I want to support, but it’s possible I’m wrong there too).