diff --git a/CHANGELOG.md b/CHANGELOG.md index 1472de3..078aeda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## Version 0.2.1 +* Correct exception properties +* [black](https://github.com/ambv/black) formatting + ## Version 0.2.0 * Change to using named parameters in most cases * Ensure all keys are handled correctly in response diff --git a/omdb/__init__.py b/omdb/__init__.py index 80b16c4..4af1379 100644 --- a/omdb/__init__.py +++ b/omdb/__init__.py @@ -1,13 +1,24 @@ -''' the omdb module ''' +""" the omdb module """ from .omdb import OMDB -from .exceptions import OMDBException, OMDBNoResults, OMDBLimitReached, OMDBTooManyResults +from .exceptions import ( + OMDBException, + OMDBNoResults, + OMDBLimitReached, + OMDBTooManyResults, +) -__author__ = 'Tyler Barrus' -__maintainer__ = 'Tyler Barrus' -__email__ = 'barrust@gmail.com' -__license__ = 'MIT' -__version__ = '0.2.0' -__url__ = 'https://github.com/barrust/pyomdbapi' -__bugtrack_url__ = '{0}/issues'.format(__url__) -__all__ = ['OMDB', 'OMDBException', 'OMDBNoResults', 'OMDBLimitReached', 'OMDBTooManyResults'] +__author__ = "Tyler Barrus" +__maintainer__ = "Tyler Barrus" +__email__ = "barrust@gmail.com" +__license__ = "MIT" +__version__ = "0.2.0" +__url__ = "https://github.com/barrust/pyomdbapi" +__bugtrack_url__ = "{0}/issues".format(__url__) +__all__ = [ + "OMDB", + "OMDBException", + "OMDBNoResults", + "OMDBLimitReached", + "OMDBTooManyResults", +] diff --git a/omdb/exceptions.py b/omdb/exceptions.py index c5264b3..6d8be4a 100644 --- a/omdb/exceptions.py +++ b/omdb/exceptions.py @@ -1,104 +1,112 @@ -''' Exceptions for the pyomdbapi project ''' +""" Exceptions for the pyomdbapi project """ class OMDBException(Exception): - ''' Base OMDB Exception + """ Base OMDB Exception Args: message (str): The exception message - ''' + """ def __init__(self, message): - ''' init ''' + """ init """ self._message = message super(OMDBException, self).__init__(self.message) @property def message(self): - ''' str: The exception message ''' + """ str: The exception message """ return self._message class OMDBInvalidAPIKey(OMDBException): - ''' Invalide API Key provided + """ Invalide API Key provided Args: api_key (str): The API Key used that generated the exception - ''' + """ def __init__(self, api_key): self._api_key = api_key - super(OMDBInvalidAPIKey, self).__init__('Invalid API Key ({}) provided'.format(self.api_key)) + super(OMDBInvalidAPIKey, self).__init__( + "Invalid API Key ({}) provided".format(self.api_key) + ) @property def api_key(self): - ''' str: The exception message ''' + """ str: The exception message """ return self._api_key class OMDBNoResults(OMDBException): - ''' A No results returned exception + """ A No results returned exception Args: error (str): The error message returned by the OMDB API service params (dict): The parameters used when the exception was raised - ''' + """ def __init__(self, error, params): - ''' init ''' + """ init """ self._params = params self._error = error - super(OMDBNoResults, self).__init__('\n\tmessage:\t{}\n\tparams: \t{}'.format(self.error, self.params)) + super(OMDBNoResults, self).__init__( + "\n\tmessage:\t{}\n\tparams: \t{}".format(self.error, self.params) + ) @property def error(self): - ''' str: The OMDB API exception message ''' + """ str: The OMDB API exception message """ return self._error @property def params(self): - ''' dict: The parameters used when the exception was raised ''' + """ dict: The parameters used when the exception was raised """ return self._params class OMDBTooManyResults(OMDBException): - ''' Too many results would be returned (per the OMDB API) + """ Too many results would be returned (per the OMDB API) Args: error (str): The error message returned by the OMDB API service params (dict): The parameters used when the exception was raised - ''' + """ def __init__(self, error, params): - ''' init ''' + """ init """ self._params = params self._error = error - super(OMDBTooManyResults, self).__init__('\n\tmessage:\t{}\n\tparams: \t{}'.format(self.error, self.params)) + super(OMDBTooManyResults, self).__init__( + "\n\tmessage:\t{}\n\tparams: \t{}".format(self.error, self.params) + ) @property def error(self): - ''' str: The OMDB API exception message ''' + """ str: The OMDB API exception message """ return self._error @property def params(self): - ''' dict: The parameters used when the exception was raised ''' + """ dict: The parameters used when the exception was raised """ return self._params class OMDBLimitReached(OMDBException): - ''' Reached the limit of requests for the user - API Key combination + """ Reached the limit of requests for the user - API Key combination Args: api_key (str): The API Key used when connecting to the OMDB API service - ''' + """ def __init__(self, api_key): - ''' init ''' + """ init """ self._api_key = api_key - super(OMDBLimitReached, self).__init__('Limit reached for API Key: {}'.format(self.api_key)) + super(OMDBLimitReached, self).__init__( + "Limit reached for API Key: {}".format(self.api_key) + ) @property def api_key(self): - ''' str: The OMDB API API key used ''' + """ str: The OMDB API API key used """ return self._api_key diff --git a/omdb/omdb.py b/omdb/omdb.py index 4af2b65..f2dcd69 100644 --- a/omdb/omdb.py +++ b/omdb/omdb.py @@ -1,14 +1,20 @@ -''' OMDB API python wrapper library ''' +""" OMDB API python wrapper library """ from math import ceil import requests -from .exceptions import OMDBException, OMDBNoResults, OMDBLimitReached, OMDBTooManyResults, OMDBInvalidAPIKey +from .exceptions import ( + OMDBException, + OMDBNoResults, + OMDBLimitReached, + OMDBTooManyResults, + OMDBInvalidAPIKey, +) from .utilities import camelcase_to_snake_case, range_inclusive, to_int class OMDB(object): - ''' The OMDB API wrapper instance + """ The OMDB API wrapper instance Args: api_key (str): The API Key to use for the requests @@ -18,13 +24,13 @@ class OMDB(object): Returns: OMDB: An OMDB API wrapper connection object Note: - With `strict` disabled, it is up to the user to check for and handle errors ''' + With `strict` disabled, it is up to the user to check for and handle errors """ - __slots__ = ['_api_url', '_timeout', '_api_key', '_session', '_strict'] + __slots__ = ["_api_url", "_timeout", "_api_key", "_session", "_strict"] def __init__(self, api_key, timeout=5, strict=True): - ''' the init object ''' - self._api_url = 'https://www.omdbapi.com/' + """ the init object """ + self._api_url = "https://www.omdbapi.com/" self._timeout = None self.timeout = timeout self._api_key = None @@ -34,19 +40,19 @@ def __init__(self, api_key, timeout=5, strict=True): self._session = requests.Session() def close(self): - ''' Close the requests connection if necessary ''' + """ Close the requests connection if necessary """ if self._session: self._session.close() self._session = None @property def api_key(self): - ''' str: The API Key to use to connect to the OMDB API ''' + """ str: The API Key to use to connect to the OMDB API """ return self._api_key @api_key.setter def api_key(self, val): - ''' set the API Key ''' + """ set the API Key """ if isinstance(val, str): self._api_key = val else: @@ -54,30 +60,32 @@ def api_key(self, val): @property def timeout(self): - ''' float: The timeout parameter to pass to requests for how long to wait ''' + """ float: The timeout parameter to pass to requests for how long to wait """ return self._timeout @timeout.setter def timeout(self, val): - ''' set the timeout property ''' + """ set the timeout property """ try: self._timeout = float(val) except ValueError: - msg = "OMDB Timeout must be a float or convertable to float! {} provided".format(val) + msg = "OMDB Timeout must be a float or convertable to float! {} provided".format( + val + ) raise ValueError(msg) @property def strict(self): - ''' bool: Whether to throw or swallow errors; True will throw exceptions ''' + """ bool: Whether to throw or swallow errors; True will throw exceptions """ return self._strict @strict.setter def strict(self, val): - ''' set the strict property ''' + """ set the strict property """ self._strict = bool(val) def search(self, title, pull_all_results=True, page=1, **kwargs): - ''' Perform a search based on title + """ Perform a search based on title Args: title (str): The query string to lookup @@ -87,33 +95,41 @@ def search(self, title, pull_all_results=True, page=1, **kwargs): Returns: dict: A dictionary of all the results Note: - If `pull_all_results` is `True` then page is ignored ''' - params = {'s': title, 'page': 1, 'apikey': self.api_key} # set to the default... + If `pull_all_results` is `True` then page is ignored """ + params = { + "s": title, + "page": 1, + "apikey": self.api_key, + } # set to the default... if not pull_all_results: - params['page'] = page # we are going to set it so that we can pull everything! + params[ + "page" + ] = page # we are going to set it so that we can pull everything! params.update(kwargs) results = self._get_response(params) - total_results = int(results.get('total_results', 0)) - if not pull_all_results or total_results <= 10: # 10 is the max that it will ever return + total_results = int(results.get("total_results", 0)) + if ( + not pull_all_results or total_results <= 10 + ): # 10 is the max that it will ever return return results - if 'search' not in results: - results['search'] = list() # defensive + if "search" not in results: + results["search"] = list() # defensive max_i = ceil(total_results / 10) for i in range_inclusive(2, max_i): - params.update({'page': i}) + params.update({"page": i}) data = self._get_response(params) - results['search'].extend(data.get('search', list())) + results["search"].extend(data.get("search", list())) return results def get(self, *, title=None, imdbid=None, **kwargs): - ''' Retrieve a specific movie, series, or episode + """ Retrieve a specific movie, series, or episode Args: title (str): The title of the movie, series, or episode to return @@ -124,12 +140,12 @@ def get(self, *, title=None, imdbid=None, **kwargs): Raises: OMDBException: Raised when both title or imdbid is not provided Note: - Either `title` or `imdbid` is required ''' - params = {'apikey': self.api_key} + Either `title` or `imdbid` is required """ + params = {"apikey": self.api_key} if imdbid: - params['i'] = imdbid + params["i"] = imdbid elif title: - params['t'] = title + params["t"] = title else: raise OMDBException("Either title or imdbid is required!") @@ -138,7 +154,7 @@ def get(self, *, title=None, imdbid=None, **kwargs): return self._get_response(params) def search_movie(self, title, pull_all_results=True, page=1, **kwargs): - ''' Search for a movie by title + """ Search for a movie by title Args: title (str): The name, or part of a name, of the movie to look up @@ -146,13 +162,13 @@ def search_movie(self, title, pull_all_results=True, page=1, **kwargs): page (int): The page of results to return kwargs (dict): the kwargs to add additional parameters to the API request Returns: - dict: A dictionary of all the results ''' - params = {'type': 'movie'} + dict: A dictionary of all the results """ + params = {"type": "movie"} params.update(kwargs) return self.search(title, pull_all_results, page, **params) def search_series(self, title, pull_all_results=True, page=1, **kwargs): - ''' Search for a TV series by title + """ Search for a TV series by title Args: title (str): The name, or part of a name, of the TV series to look up @@ -160,13 +176,13 @@ def search_series(self, title, pull_all_results=True, page=1, **kwargs): page (int): The page of results to return kwargs (dict): the kwargs to add additional parameters to the API request Returns: - dict: A dictionary of all the results ''' - params = {'type': 'series'} + dict: A dictionary of all the results """ + params = {"type": "series"} params.update(kwargs) return self.search(title, pull_all_results, page, **params) def get_movie(self, *, title=None, imdbid=None, **kwargs): - ''' Retrieve a movie by title or IMDB id + """ Retrieve a movie by title or IMDB id Args: title (str): The name of the movie to retrieve @@ -175,13 +191,13 @@ def get_movie(self, *, title=None, imdbid=None, **kwargs): Returns: dict: A dictionary of all the results Note: - Either `title` or `imdbid` is required ''' - params = {'type': 'movie'} + Either `title` or `imdbid` is required """ + params = {"type": "movie"} params.update(kwargs) return self.get(title=title, imdbid=imdbid, **params) def get_series(self, *, title=None, imdbid=None, pull_episodes=False, **kwargs): - ''' Retrieve a TV series information by title or IMDB id + """ Retrieve a TV series information by title or IMDB id Args: title (str): The name of the TV series to retrieve @@ -191,24 +207,24 @@ def get_series(self, *, title=None, imdbid=None, pull_episodes=False, **kwargs): Returns: dict: A dictionary of all the results Note: - Either `title` or `imdbid` is required ''' - params = {'type': 'series'} + Either `title` or `imdbid` is required """ + params = {"type": "series"} params.update(kwargs) res = self.get(title=title, imdbid=imdbid, **params) num_seasons = 0 if pull_episodes: - num_seasons = to_int(res.get('total_seasons', 0)) - res['seasons'] = dict() + num_seasons = to_int(res.get("total_seasons", 0)) + res["seasons"] = dict() for i in range(num_seasons): season_num = i + 1 season = self.get_episodes(title=title, imdbdid=imdbid, season=season_num) - res['seasons'][season_num] = season + res["seasons"][season_num] = season return res def get_episode(self, *, title=None, imdbid=None, season=1, episode=1, **kwargs): - ''' Retrieve a TV series episode by title or IMDB id and season and episode number + """ Retrieve a TV series episode by title or IMDB id and season and episode number Args: title (str): The name of the TV series to retrieve @@ -219,17 +235,17 @@ def get_episode(self, *, title=None, imdbid=None, season=1, episode=1, **kwargs) Returns: dict: A dictionary of all the results Note: - Either `title` or `imdbid` is required ''' - params = {'type': 'episode'} + Either `title` or `imdbid` is required """ + params = {"type": "episode"} if season: - params['Season'] = season + params["Season"] = season if episode: - params['Episode'] = episode + params["Episode"] = episode params.update(kwargs) return self.get(title=title, imdbid=imdbid, **params) def get_episodes(self, *, title=None, imdbid=None, season=1, **kwargs): - ''' Retrieve all episodes of a TV series by season number + """ Retrieve all episodes of a TV series by season number Args: title (str): The name of the TV series to retrieve @@ -239,19 +255,22 @@ def get_episodes(self, *, title=None, imdbid=None, season=1, **kwargs): Returns: dict: A dictionary of all the results Note: - Either `title` or `imdbid` is required ''' - return self.get_episode(title=title, imdbid=imdbid, season=season, episode=None, **kwargs) + Either `title` or `imdbid` is required """ + return self.get_episode( + title=title, imdbid=imdbid, season=season, episode=None, **kwargs + ) def _get_response(self, kwargs): - ''' wrapper for the `requests` library call ''' - # response = self._session.get(self._api_url, params=kwargs, timeout=self._timeout).json(encoding='utf8') - response = self._session.get(self._api_url, params=kwargs, timeout=self._timeout).json() + """ wrapper for the `requests` library call """ + response = self._session.get( + self._api_url, params=kwargs, timeout=self._timeout + ).json() return self.__format_results(response, kwargs) def __format_results(self, res, params): - ''' format the results into non-camelcase dictionaries ''' + """ format the results into non-camelcase dictionaries """ if not isinstance(res, dict): - raise TypeError('Expecting dict type, recieved {}'.format(type(res))) + raise TypeError("Expecting dict type, recieved {}".format(type(res))) keys = sorted([x for x in res.keys()]) for key in keys: @@ -271,16 +290,16 @@ def __format_results(self, res, params): res[camelcase_to_snake_case(key)] = val # NOTE: I dislike having to use string comparisons to check for specific error conditions - if self.strict and 'response' in res and res['response'] == 'False': - err = res.get('error', '').lower() - if err == 'too many results.': - raise OMDBTooManyResults(res['error'], params) - elif err in ('movie not found!', 'series or season not found!'): - raise OMDBNoResults(res['error'], params) - elif err == 'request limit reached!': + if self.strict and "response" in res and res["response"] == "False": + err = res.get("error", "").lower() + if err == "too many results.": + raise OMDBTooManyResults(res["error"], params) + elif err in ("movie not found!", "series or season not found!"): + raise OMDBNoResults(res["error"], params) + elif err == "request limit reached!": raise OMDBLimitReached(self.api_key) - elif err == 'invalid api key!': + elif err == "invalid api key!": raise OMDBInvalidAPIKey(self.api_key) - raise OMDBException('An unknown exception was returned: {}'.format(err)) + raise OMDBException("An unknown exception was returned: {}".format(err)) return res diff --git a/omdb/utilities.py b/omdb/utilities.py index 62e3115..7299e82 100644 --- a/omdb/utilities.py +++ b/omdb/utilities.py @@ -1,18 +1,18 @@ -''' A utilitites suite ''' +""" A utilitites suite """ def camelcase_to_snake_case(_input): - ''' Convert a camel case string to a snake case string: CamelCase -> camel_case + """ Convert a camel case string to a snake case string: CamelCase -> camel_case Args: - _input (str): The string to convert ''' + _input (str): The string to convert """ # https://codereview.stackexchange.com/a/185974 res = _input[0].lower() for i, letter in enumerate(_input[1:], 1): if letter.isupper(): try: if _input[i - 1].islower() or _input[i + 1].islower(): - res += '_' + res += "_" except IndexError: pass res += letter.lower() @@ -20,7 +20,7 @@ def camelcase_to_snake_case(_input): def range_inclusive(start, end, step=1): - ''' Return the range of elements inclusive of the end value + """ Return the range of elements inclusive of the end value Args: start (int): The start value of the range @@ -28,19 +28,19 @@ def range_inclusive(start, end, step=1): step (int): The step value to use Yields: int: The current value within the range - ''' + """ for i in range(start, end + 1, step): yield i def to_int(val): - ''' Turn the passed in variable into an int; returns 0 if errors + """ Turn the passed in variable into an int; returns 0 if errors Args: val (str): The variable to turn into an int Returns: int: The int value if possible, 0 if an error occurs - ''' + """ try: return int(val) except ValueError: