Coverage for fio_wrapper/config.py: 100%
108 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-16 12:21 +0100
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-16 12:21 +0100
1import os
2import logging
3import yaml
4import importlib.util
5from typing import Optional
6from typing import List
7from typing import Dict
8from fio_wrapper.exceptions import UnknownConfig
10logger = logging.getLogger(__name__)
13class Config:
14 """FIO Wrapper configuration class
16 Attributes:
17 _api_key (str, optional): FIO API key
18 _version (str, optional): FIO API version
19 _application (str, optional): Application name
20 _base_url (str, optional): FIO base url
21 _timeout (float, optional): FIO generic timeout
22 _ssl_verify (bool, optional): Request ssl verification
24 data (ConfigParser): Configuration Parser
26 """
28 # https://stackoverflow.com/a/15836901
29 def data_merge(self, a, b):
30 """merges b into a and return merged result
32 Args:
33 a (Dict): First dictionary
34 b (Dict): Second dictionary
36 NOTE:
37 tuples and arbitrary objects are not handled as it is totally ambiguous what should happen
38 """
39 key = None
40 # ## debug output
41 # sys.stderr.write("DEBUG: %s to %s\n" %(b,a))
43 if (
44 a is None
45 or isinstance(a, str)
46 or isinstance(a, int)
47 or isinstance(a, float)
48 ):
49 # border case for first run or if a is a primitive
50 a = b
51 elif isinstance(a, list):
52 # lists can be only appended
53 if isinstance(b, list):
54 # merge lists
55 a.extend(b)
56 else:
57 # append to list
58 a.append(b)
59 elif isinstance(a, dict):
60 # dicts must be merged
61 if isinstance(b, dict):
62 for key in b:
63 if key in a:
64 a[key] = self.data_merge(a[key], b[key])
65 else:
66 a[key] = b[key]
67 else:
68 raise Exception('Cannot merge non-dict "%s" into dict "%s"' % (b, a))
69 else:
70 raise Exception('NOT IMPLEMENTED "%s" into "%s"' % (b, a))
72 return a
74 def __init__(
75 self,
76 api_key: Optional[str] = None,
77 version: Optional[str] = None,
78 application: Optional[str] = None,
79 base_url: Optional[str] = None,
80 timeout: Optional[float] = 10.0,
81 ssl_verify: Optional[bool] = True,
82 user_config: Optional[str] = None,
83 ) -> None:
84 """Initializes the configuration
86 Args:
87 api_key (Optional[str]): FIO API key. Defaults to None.
88 version (Optional[str]): FIO API version. Defaults to None.
89 application (Optional[str]): Application name. Defaults to None.
90 base_url (Optional[str]): FIO base url. Defaults to None.
91 timeout (Optional[float]): FIO generic timeout. Defaults to 10.0.
92 ssl_verify (Optional[bool]): Request ssl verification. Defaults to True.
93 user_config (Optional[str]): User configuration file. Defaults to None.
94 """
95 # FIO instantiation overwrites
96 self._api_key = api_key
97 self._version = version
98 self._application = application
99 self._base_url = base_url
100 self._timeout = timeout
101 self._ssl_verify = ssl_verify
103 # initialize data
104 self._base_file = os.path.join(os.path.dirname(__file__), "base.yml")
106 # base configuration
107 with open(self._base_file, "r") as base_file:
108 self.data = yaml.safe_load(base_file)
110 # user config, if provided
111 if user_config is not None:
112 with open(user_config, "r") as user_file:
113 user = yaml.safe_load(user_file)
115 # self.data = self.dict_merge(self.data, user)
116 self.data = self.data_merge(self.data, user)
118 @property
119 def versions(self) -> List[str]:
120 """Gets the versions information from config
122 Raises:
123 SystemExit: No list of available FIO versions provided
125 Returns:
126 List[str]: List of versions
127 """
128 return self.data["fio"]["versions"]
130 @property
131 def api_key(self) -> str:
132 """Gets the FIO API key
134 Returns:
135 str: FIO API key or None
136 """
137 if self._api_key is not None:
138 return self._api_key
140 try:
141 return self.data["fio"]["api_key"]
142 except KeyError:
143 # API Key can be blank
144 return None
146 @property
147 def version(self) -> str:
148 """Gets the FIO version specified
150 Returns:
151 str: FIO API version
152 """
153 if self._version is not None:
154 return self._version
156 return self.data["fio"]["version"]
158 @property
159 def application(self) -> str:
160 """Gets the application name
162 Returns:
163 str: Application name
164 """
165 if self._application is not None:
166 return self._application
168 return self.data["fio"]["application"]
170 @property
171 def base_url(self) -> str:
172 """Gets the FIO base url
174 Returns:
175 str: FIO base url
176 """
177 if self._base_url is not None:
178 return self._base_url
180 return self.data["fio"]["base_url"]
182 @property
183 def timeout(self) -> float:
184 """Gets the timeout parameter
186 Returns:
187 float: Timeout parameter
188 """
189 if self._timeout is not None:
190 return self._timeout
192 return self.data["fio"]["timeout"]
194 @property
195 def ssl_verify(self) -> float:
196 """Gets the ssl verification parameter
198 Returns:
199 float: Seconds as float of request timeout
200 """
201 if self._ssl_verify is not None:
202 return self._ssl_verify
204 return self.data["fio"]["ssl_verify"]
206 @property
207 def cache(self) -> bool:
208 """Gets the cache usage status
210 Returns:
211 bool: Cache used, true or false
212 """
213 return self.data["cache"]["enabled"]
215 @property
216 def cache_default_expire(self) -> int:
217 """Gets the cache default expiration time
219 Returns:
220 int: Expiration time in seconds
221 """
222 return self.data["cache"]["default_expire"]
224 def get(self, section: str, option: str) -> str:
225 """Gets a configuration element
227 Args:
228 section (str): Configuration section
229 option (str): Configuration option
231 Raises:
232 UnknownConfig: Configuration not found
234 Returns:
235 str: Configuration element
236 """
237 logger.debug("get(): %s | %s", section, option)
238 try:
239 return self.data[section][option]
240 except KeyError as exc:
241 raise UnknownConfig() from exc
243 def get_url(self, option: str) -> str:
244 """Gets a url configuration element
246 Args:
247 option (str): Configuration option
249 Returns:
250 str: URL configuration parameter
251 """
253 try:
254 return self.data["fio_urls"][self.version][option]
255 except KeyError as exc:
256 raise UnknownConfig() from exc
258 def cache_url_expirations(self) -> Dict[str, any]:
259 """Creates the dict for requests_cache url expirations
261 Returns:
262 Dict[str, any]: URL specific expiration settings
263 """
264 # check if requests-cache is installed
265 if not self.cache or importlib.util.find_spec("requests_cache") is None:
266 return {}
268 from requests_cache import DO_NOT_CACHE, NEVER_EXPIRE
270 expiration_list = {}
272 for url, expiration in self.data.get("cache", {}).get("urls", {}).items():
273 if isinstance(expiration, int):
274 expiration_list[url] = expiration
275 elif expiration == "NEVER_EXPIRE":
276 expiration_list[url] = NEVER_EXPIRE
277 elif expiration == "DO_NOT_CACHE":
278 expiration_list[url] = DO_NOT_CACHE
279 else:
280 logger.warning(
281 "Unknown expiration configuration: %s | %s", url, expiration
282 )
284 return expiration_list