Coverage for youversion/models/base.py: 100%

13 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-06-26 11:31 +0100

1from typing import Any, Protocol 

2 

3try: 

4 from typing import TypeAlias 

5except ImportError: 

6 # Python < 3.10 compatibility 

7 from typing import TypeAlias 

8 

9 

10class ReferenceProtocol(Protocol): 

11 """Protocol for YouVersion Bible references.""" 

12 

13 version_id: str | int 

14 human: str 

15 usfm: str | list[str] 

16 

17 def __getattr__(self, name: str) -> Any: 

18 """Allow access to dynamically added fields.""" 

19 class_name = self.__class__.__name__ 

20 raise AttributeError(f"'{class_name}' has no attribute '{name}'") 

21 

22 

23# Type alias for convenience 

24Reference: TypeAlias = ReferenceProtocol 

25 

26 

27class MomentProtocol(Protocol): 

28 """Protocol for dynamically created moment objects from YouVersion API. 

29 

30 This protocol describes the structure of moments returned by the API, 

31 allowing type checkers to understand the expected fields while 

32 supporting dynamically generated dataclasses. 

33 """ 

34 

35 # Core moment fields 

36 id: int 

37 kind_id: str 

38 kind_color: str | None 

39 created_dt: str | None # ISO datetime string 

40 updated_dt: str | None # ISO datetime string 

41 

42 # Base moment information 

43 base: dict[str, Any] | None 

44 # base structure typically contains: 

45 # - title: dict with l_str and l_args 

46 # - body: Optional[str] 

47 # - images: dict with avatar, icon, body 

48 # - action_url: Optional[str] 

49 # - share_url: Optional[str] 

50 

51 # Extras information (varies by moment type) 

52 extras: dict[str, Any] | None 

53 # extras structure typically contains: 

54 # - user: dict with id, username, name, avatar 

55 # - title: Optional[str] 

56 # - content: Optional[str] 

57 # - color: Optional[str] 

58 # - references: Optional[list[dict]] with usfm, version_id, human 

59 # - user_status: Optional[str] 

60 # - system_status: Optional[str] 

61 # - language_tag: Optional[str] 

62 # - labels: Optional[list] 

63 

64 # Commenting information 

65 commenting: dict[str, Any] | None 

66 # commenting structure: 

67 # - enabled: bool 

68 # - total: int 

69 # - comments: Optional[list] 

70 

71 # Liking information 

72 liking: dict[str, Any] | None 

73 # liking structure: 

74 # - enabled: bool 

75 # - total: int 

76 # - likes: Optional[list] 

77 # - all_users: Optional[list] 

78 

79 def __getattr__(self, name: str) -> Any: 

80 """Allow access to dynamically added fields. 

81 

82 This enables the protocol to work with dynamically generated 

83 dataclasses that may have additional fields not defined here. 

84 """ 

85 class_name = self.__class__.__name__ 

86 raise AttributeError(f"'{class_name}' has no attribute '{name}'") 

87 

88 

89# Type alias for convenience 

90Moment: TypeAlias = MomentProtocol