"""
Contains base dictionary API client interface.
.. class:: BaseDictionaryApiClientInterface(abc.ABC)
"""
import abc
import logging
from http import HTTPStatus
import typing
from ..errors import (
API_ERRORS_MAPPER,
DictionaryApiError
)
from ..languages import (
DEFAULT_LANGUAGE_CODE,
LanguageCodes
)
from ..parsers import DictionaryApiErrorParser
from ..urls import ApiUrl
__all__ = ['BaseDictionaryApiClientInterface']
logger = logging.getLogger(__name__)
[docs]class BaseDictionaryApiClientInterface(abc.ABC):
"""
Implements base dictionary API client interface.
Abstract client interface that supposed to be inherited
for ``sync`` and ``async`` **base** clients.
Here, interface:
Class that implements some useful API
and makes sense in inheritance
for other base clients
that also must implement this base API
but also provide some specific
such concrete abstract methods.
So, for implementing of ``sync`` and ``async`` base clients
base API is not repeated in code
but provided with inheritance from this interface.
"""
[docs] def __init__(self, default_language_code: LanguageCodes = DEFAULT_LANGUAGE_CODE) -> None:
"""
Init base dictionary API client instance.
:param default_language_code: default language of the searched words for the client
:type default_language_code: :obj:`LanguageCodes`
:raise:
:TypeError: if has been passed unsupported ``default_language_code``
"""
self._default_language_code = default_language_code
if not isinstance(self._default_language_code, LanguageCodes):
message = (
'For `language_code` has been passed object with unsupported type. '
'Expected to get argument with type `freedictionaryapi.languages.LanguageCodes`! '
f'Got (language_code={self._default_language_code!r})'
)
raise TypeError(message)
def __repr__(self) -> str:
class_name = self.__class__.__name__
return f'{class_name}(default_language_code={self._default_language_code!r})'
@property
def default_language_code(self) -> LanguageCodes:
"""
:return: default client language code
:rtype: :obj:`LanguageCodes`
"""
return self._default_language_code
[docs] @staticmethod
def _analyze_response(url: str, status_code: int, response: typing.Union[dict, list]) -> typing.Union[dict, list]:
"""
Analyze API response.
Do this:
- log about response status (successful | unsuccessful);
- raise correspond error if response is not successful.
:param url: URL that generated for API request
:type url: :obj:`str`
:param status_code: response status code
:type status_code: :obj:`int`
:param response: API response that loaded in python object
:type response: :obj:`Union[dict, list]`
:return: passed response
:rtype: :obj:`Union[dict, list]`
:raise:
:DictionaryApiError: when unsuccessful status code got of API request
"""
if status_code != HTTPStatus.OK:
# get error type by status code from error mapper
# by default get common error
error = API_ERRORS_MAPPER.get(status_code, DictionaryApiError)
error_parser = DictionaryApiErrorParser(status_code, response)
error_message = error_parser.get_formatted_error_message()
logger.info(f'Response is not successful [code={status_code!r}] from url: {url!r}.')
raise error(error_message)
logger.info(f'Response is successful [code={status_code}] from url: {url}.')
return response
[docs] def _generate_url(self, word: str, language_code: typing.Optional[LanguageCodes] = None
) -> typing.Tuple[str, LanguageCodes]:
"""
Generate URL for API request.
:param word: searched word
:type word: :obj:`str`
:param language_code: language of the searched word
:type language_code: :obj:`Optional[LanguageCodes]`
:return: tuple of:
- generated URL;
- used language code.
:rtype: :obj:`Union[str, LanguageCodes]`
"""
language_code: LanguageCodes = self._default_language_code if language_code is None else language_code
url = ApiUrl(word, language_code=language_code).get_url()
return (url, language_code)