Coverage for src/lexigram/notification/module.py: 100%
16 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 02:32 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 02:32 +0800
1"""NotificationModule — IoC module for lexigram-notification."""
3from __future__ import annotations
5from typing import TYPE_CHECKING, Any
7from lexigram.contracts.notification.protocols import (
8 PushChannelProtocol,
9 SMSChannelProtocol,
10)
11from lexigram.di.module import DynamicModule, Module, module
13if TYPE_CHECKING:
14 from lexigram.notification.config import NotificationConfig
17@module()
18class NotificationModule(Module):
19 """SMS and push notification integration with Named DI multi-backend support.
21 Registers :class:`~lexigram.contracts.notification.protocols.SMSChannelProtocol`
22 and :class:`~lexigram.contracts.notification.protocols.PushChannelProtocol` for
23 constructor injection.
25 Usage::
27 from lexigram.notification.config import NotificationConfig
28 from lexigram.notification.module import NotificationModule
30 @module(
31 imports=[NotificationModule.configure(NotificationConfig(backends=[...]))]
32 )
33 class AppModule(Module):
34 pass
36 Named injection::
38 class MyService:
39 def __init__(
40 self,
41 sms: SMSChannelProtocol, # primary
42 alerts_sms: Annotated[SMSChannelProtocol, Named("alerts")], # named
43 push: PushChannelProtocol, # primary
44 mobile_push: Annotated[PushChannelProtocol, Named("mobile")], # named
45 ) -> None: ...
46 """
48 @classmethod
49 def configure(cls, config: NotificationConfig | Any | None = None) -> DynamicModule:
50 """Create a NotificationModule with explicit configuration.
52 Args:
53 config: :class:`~lexigram.notification.config.NotificationConfig` or ``None``
54 to use defaults (reads from environment variables).
56 Returns:
57 A :class:`~lexigram.di.module.DynamicModule` descriptor.
58 """
59 from lexigram.notification.di.provider import NotificationProvider
61 return DynamicModule(
62 module=cls,
63 providers=[NotificationProvider(config=config)],
64 exports=[SMSChannelProtocol, PushChannelProtocol],
65 )
67 @classmethod
68 def stub(cls, config: Any = None) -> DynamicModule:
69 """Return a NotificationModule for unit tests — no messages sent.
71 Uses an empty config (no backends configured) so all notifications
72 are dropped and no external services are contacted.
74 Returns:
75 A :class:`~lexigram.di.module.DynamicModule` descriptor.
76 """
77 from lexigram.notification.config import NotificationConfig
78 from lexigram.notification.di.provider import NotificationProvider
80 return DynamicModule(
81 module=cls,
82 providers=[NotificationProvider(config=NotificationConfig())],
83 exports=[SMSChannelProtocol, PushChannelProtocol],
84 )
87__all__ = ["NotificationModule"]