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

43 statements  

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

1"""Bible-related data models.""" 

2 

3from typing import Any, Protocol 

4 

5try: 

6 from typing import TypeAlias 

7except ImportError: 

8 # Python < 3.10 compatibility 

9 from typing import TypeAlias 

10 

11 

12class LanguageProtocol(Protocol): 

13 """Protocol for language information.""" 

14 

15 id: int 

16 name: str 

17 language_tag: str 

18 local_name: str | None 

19 

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

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

22 class_name = self.__class__.__name__ 

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

24 

25 

26# Type alias for convenience 

27Language: TypeAlias = LanguageProtocol 

28 

29 

30class PublisherProtocol(Protocol): 

31 """Protocol for publisher information.""" 

32 

33 id: int 

34 name: str 

35 url: str | None 

36 

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

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

39 class_name = self.__class__.__name__ 

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

41 

42 

43# Type alias for convenience 

44Publisher: TypeAlias = PublisherProtocol 

45 

46 

47class BookProtocol(Protocol): 

48 """Protocol for Bible book information.""" 

49 

50 id: int 

51 name: str 

52 abbreviation: str 

53 chapters: list[int] 

54 testament: str | None 

55 

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

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

58 class_name = self.__class__.__name__ 

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

60 

61 

62# Type alias for convenience 

63Book: TypeAlias = BookProtocol 

64 

65 

66class VersionProtocol(Protocol): 

67 """Protocol for Bible version information.""" 

68 

69 id: int 

70 title: str 

71 abbreviation: str 

72 language: Language 

73 publisher: Publisher 

74 books: list[Book] 

75 text: bool 

76 audio: bool 

77 copyright_short: str | None 

78 copyright_long: str | None 

79 local_title: str | None 

80 local_abbreviation: str | None 

81 language_tag_selected: str | None 

82 last_modified: int | None 

83 metadata_build: int | None 

84 reader_footer: str | None 

85 reader_footer_url: str | None 

86 

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

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

89 class_name = self.__class__.__name__ 

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

91 

92 

93# Type alias for convenience 

94Version: TypeAlias = VersionProtocol 

95 

96 

97class ChapterProtocol(Protocol): 

98 """Protocol for chapter information.""" 

99 

100 usfm: str 

101 human: str 

102 canonical: bool 

103 toc: bool 

104 

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

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

107 class_name = self.__class__.__name__ 

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

109 

110 

111# Type alias for convenience 

112Chapter: TypeAlias = ChapterProtocol 

113 

114 

115class ChapterContentProtocol(Protocol): 

116 """Protocol for chapter content with verses.""" 

117 

118 id: int 

119 reference: str 

120 content: str 

121 verses: list[str] 

122 chapters: list[Chapter] 

123 version: Version 

124 

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

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

127 class_name = self.__class__.__name__ 

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

129 

130 

131# Type alias for convenience 

132ChapterContent: TypeAlias = ChapterContentProtocol 

133 

134 

135class ConfigurationProtocol(Protocol): 

136 """Protocol for Bible configuration.""" 

137 

138 versions: list[Version] 

139 languages: list[Language] 

140 stylesheets: list[dict] | None 

141 

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

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

144 class_name = self.__class__.__name__ 

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

146 

147 

148# Type alias for convenience 

149Configuration: TypeAlias = ConfigurationProtocol 

150 

151 

152class RecommendedLanguagesProtocol(Protocol): 

153 """Protocol for recommended languages for a country.""" 

154 

155 languages: list[Language] 

156 country: str 

157 

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

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

160 class_name = self.__class__.__name__ 

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

162 

163 

164# Type alias for convenience 

165RecommendedLanguages: TypeAlias = RecommendedLanguagesProtocol