Compare commits

...

1 Commits

Author SHA1 Message Date
lebaudantoine 976abba1d6 ♻️(backend) refactor analytics backend from Protocol to abstract class
Switch the analytics backend interface from a typing Protocol to an
abstract base class, so the contract that backends must implement
is explicit and enforced at instantiation time.
2026-07-07 11:46:03 +02:00
3 changed files with 10 additions and 9 deletions
+1
View File
@@ -14,6 +14,7 @@ and this project adheres to
- ⬆️(dependencies) update python dependencies - ⬆️(dependencies) update python dependencies
- 💥(summary) remove v1 related code #1362 - 💥(summary) remove v1 related code #1362
- ✨(meet) use compatible with summary v2 #1362 - ✨(meet) use compatible with summary v2 #1362
- ♻️(backend) refactor analytics backend from Protocol to abstract class
### Fixed ### Fixed
+7 -8
View File
@@ -1,12 +1,13 @@
"""Analytics backend protocol and default no-op implementation.""" """Analytics backend protocol and default no-op implementation."""
from typing import Any, Protocol from abc import ABC, abstractmethod
from typing import Any
from ..models import User from ..models import User
from .events import AnalyticsEvent from .events import AnalyticsEvent
class AnalyticsBackend(Protocol): class AnalyticsBackend(ABC):
""" """
Interface every analytics backend must implement. Interface every analytics backend must implement.
@@ -17,11 +18,11 @@ class AnalyticsBackend(Protocol):
ANALYTICS_BACKEND_SETTINGS = {"api_key": "...", "host": "..."} ANALYTICS_BACKEND_SETTINGS = {"api_key": "...", "host": "..."}
""" """
def __init__(self, **kwargs: Any) -> None: ... @abstractmethod
def identify(self, user: User, properties: dict[str, Any] | None = None) -> None: def identify(self, user: User, properties: dict[str, Any] | None = None) -> None:
"""Associate traits (email, name, ...) with an identified user.""" """Associate traits (email, name, ...) with an identified user."""
@abstractmethod
def capture( def capture(
self, self,
user: User, user: User,
@@ -30,16 +31,14 @@ class AnalyticsBackend(Protocol):
) -> None: ) -> None:
"""Record an event performed by an identified user.""" """Record an event performed by an identified user."""
@abstractmethod
def shutdown(self) -> None: def shutdown(self) -> None:
"""Flush pending events. Called on process exit.""" """Flush pending events. Called on process exit."""
class NoOpAnalytics: class NoOpAnalytics(AnalyticsBackend):
"""Default backend: silently discards everything.""" """Default backend: silently discards everything."""
def __init__(self, **kwargs: Any) -> None:
"""No-op: accepts and ignores any backend settings kwargs."""
def identify(self, user: User, properties=None) -> None: def identify(self, user: User, properties=None) -> None:
"""No-op: discards identify calls.""" """No-op: discards identify calls."""
+2 -1
View File
@@ -6,12 +6,13 @@ from typing import Any
from posthog import Posthog from posthog import Posthog
from ..models import User from ..models import User
from . import AnalyticsBackend
from .events import AnalyticsEvent from .events import AnalyticsEvent
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class PostHogAnalytics: class PostHogAnalytics(AnalyticsBackend):
"""Send events to PostHog, keyed on the user's primary key (UUID).""" """Send events to PostHog, keyed on the user's primary key (UUID)."""
def __init__( def __init__(