Coverage for src/lexigram/notification/config/top_level.py: 90%

21 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-26 02:26 +0800

1"""Top-level notification configuration.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass 

6from typing import ClassVar 

7 

8from lexigram.config.base import BaseConfig 

9from lexigram.notification.config.push import NamedPushConfig 

10from lexigram.notification.config.sms import NamedSMSConfig 

11from lexigram.notification.constants import ( 

12 ENV_NESTED_DELIMITER, 

13 ENV_PREFIX, 

14) 

15from lexigram.validation import ConfigDict, Field 

16 

17 

18@dataclass(init=False) 

19class NotificationConfig(BaseConfig): 

20 """Top-level notification configuration. 

21 

22 Loaded from the ``notification:`` key in application.yaml, with environment 

23 variable overrides via ``LEX_NOTIFICATION__*`` prefix. 

24 """ 

25 

26 config_section: ClassVar[str] = "notification" 

27 

28 model_config: ClassVar[ConfigDict] = ConfigDict( # type: ignore[typeddict-unknown-key] 

29 env_prefix=ENV_PREFIX, 

30 env_nested_delimiter=ENV_NESTED_DELIMITER, 

31 extra="ignore", 

32 ) 

33 

34 sms_backends: list[NamedSMSConfig] = Field( 

35 default_factory=list, 

36 description=( 

37 "Named SMS backends for multi-backend support. " 

38 "When non-empty, the provider registers each backend under " 

39 "Annotated[SMSChannelProtocol, Named(entry.name)]. " 

40 "The first entry (or the one with primary=True) also receives " 

41 "the unnamed SMSChannelProtocol binding for backward compatibility." 

42 ), 

43 ) 

44 push_backends: list[NamedPushConfig] = Field( 

45 default_factory=list, 

46 description=( 

47 "Named push notification backends for multi-backend support. " 

48 "When non-empty, the provider registers each backend under " 

49 "Annotated[PushChannelProtocol, Named(entry.name)]. " 

50 "The first entry (or the one with primary=True) also receives " 

51 "the unnamed PushChannelProtocol binding for backward compatibility." 

52 ), 

53 ) 

54 

55 @classmethod 

56 def from_named_sms(cls, entry: NamedSMSConfig) -> NotificationConfig: 

57 """Build a single-SMS-backend NotificationConfig from a NamedSMSConfig entry. 

58 

59 Used internally by NotificationProvider to create per-backend configs 

60 from a multi-backend declaration. 

61 

62 Args: 

63 entry: The named SMS backend entry to materialise. 

64 

65 Returns: 

66 A NotificationConfig configured for the single named SMS backend. 

67 """ 

68 return cls(sms_backends=[entry]) 

69 

70 @classmethod 

71 def from_named_push(cls, entry: NamedPushConfig) -> NotificationConfig: 

72 """Build a single-push-backend NotificationConfig from a NamedPushConfig entry. 

73 

74 Used internally by NotificationProvider to create per-backend configs 

75 from a multi-backend declaration. 

76 

77 Args: 

78 entry: The named push backend entry to materialise. 

79 

80 Returns: 

81 A NotificationConfig configured for the single named push backend. 

82 """ 

83 return cls(push_backends=[entry]) 

84 

85 

86__all__ = [ 

87 "NotificationConfig", 

88]