Coverage for youversion/models/common.py: 100%
48 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-26 11:31 +0100
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-26 11:31 +0100
1"""Common data models used across different API endpoints."""
3from typing import Any, Protocol
5try:
6 from typing import TypeAlias
7except ImportError:
8 # Python < 3.10 compatibility
9 from typing import TypeAlias
12class UserBaseProtocol(Protocol):
13 """Protocol for base user information."""
15 id: int
16 name: str
17 avatar: str | None
18 email: str | None
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}'")
26# Type alias for convenience
27UserBase: TypeAlias = UserBaseProtocol
30class AvatarProtocol(Protocol):
31 """Protocol for user avatar information."""
33 url: str
34 width: int | None
35 height: int | None
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}'")
43# Type alias for convenience
44Avatar: TypeAlias = AvatarProtocol
47class ImagesProtocol(Protocol):
48 """Protocol for image information."""
50 url: str
51 width: int | None
52 height: int | None
53 alt: str | None
55 def __getattr__(self, name: str) -> Any:
56 """Allow access to dynamically added fields."""
57 class_name = self.__class__.__name__
58 raise AttributeError(f"'{class_name}' has no attribute '{name}'")
61# Type alias for convenience
62Images: TypeAlias = ImagesProtocol
65class LinkProtocol(Protocol):
66 """Protocol for link information."""
68 url: str
69 text: str | None
70 target: str | None
72 def __getattr__(self, name: str) -> Any:
73 """Allow access to dynamically added fields."""
74 class_name = self.__class__.__name__
75 raise AttributeError(f"'{class_name}' has no attribute '{name}'")
78# Type alias for convenience
79Link: TypeAlias = LinkProtocol
82class LocalizeProtocol(Protocol):
83 """Protocol for localized text information."""
85 text: str
86 language_tag: str | None
88 def __getattr__(self, name: str) -> Any:
89 """Allow access to dynamically added fields."""
90 class_name = self.__class__.__name__
91 raise AttributeError(f"'{class_name}' has no attribute '{name}'")
94# Type alias for convenience
95Localize: TypeAlias = LocalizeProtocol
98class ApiErrorProtocol(Protocol):
99 """Protocol for API error information."""
101 code: str
102 message: str
103 field: str | None
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}'")
111# Type alias for convenience
112ApiError: TypeAlias = ApiErrorProtocol
115class ApiErrorsProtocol(Protocol):
116 """Protocol for multiple API errors."""
118 errors: list[ApiError]
120 def __getattr__(self, name: str) -> Any:
121 """Allow access to dynamically added fields."""
122 class_name = self.__class__.__name__
123 raise AttributeError(f"'{class_name}' has no attribute '{name}'")
126# Type alias for convenience
127ApiErrors: TypeAlias = ApiErrorsProtocol
130class PaginationInfoProtocol(Protocol):
131 """Protocol for pagination information."""
133 page: int
134 per_page: int
135 total: int
136 total_pages: int
137 has_next: bool
138 has_prev: bool
140 def __getattr__(self, name: str) -> Any:
141 """Allow access to dynamically added fields."""
142 class_name = self.__class__.__name__
143 raise AttributeError(f"'{class_name}' has no attribute '{name}'")
146# Type alias for convenience
147PaginationInfo: TypeAlias = PaginationInfoProtocol
150class ApiResponseProtocol(Protocol):
151 """Protocol for generic API response wrapper."""
153 data: Any
154 pagination: PaginationInfo | None
155 errors: list[ApiError] | None
157 def __getattr__(self, name: str) -> Any:
158 """Allow access to dynamically added fields."""
159 class_name = self.__class__.__name__
160 raise AttributeError(f"'{class_name}' has no attribute '{name}'")
163# Type alias for convenience
164ApiResponse: TypeAlias = ApiResponseProtocol