diff --git a/ipinfo/cache/default.py b/ipinfo/cache/default.py index 0d58c05..c0b12df 100644 --- a/ipinfo/cache/default.py +++ b/ipinfo/cache/default.py @@ -3,6 +3,7 @@ """ import cachetools + from .interface import CacheInterface diff --git a/ipinfo/details.py b/ipinfo/details.py index 3712d71..6bda5e8 100644 --- a/ipinfo/details.py +++ b/ipinfo/details.py @@ -2,6 +2,7 @@ Details returned by the IPinfo service. """ + class Details: """Encapsulates data for single IP address.""" @@ -14,7 +15,7 @@ def __getattr__(self, attr): if attr in self.details: return self.details[attr] else: - raise AttributeError('{} is not a valid attribute of Details'.format(attr)) + raise AttributeError("{} is not a valid attribute of Details".format(attr)) @property def all(self): diff --git a/ipinfo/exceptions.py b/ipinfo/exceptions.py index a8c3787..2cebde1 100644 --- a/ipinfo/exceptions.py +++ b/ipinfo/exceptions.py @@ -2,6 +2,8 @@ Exceptions thrown by the IPinfo service. """ + class RequestQuotaExceededError(Exception): """Error indicating that users monthly request quota has been passed.""" + pass diff --git a/ipinfo/handler.py b/ipinfo/handler.py index 0473f58..a4cc3fa 100644 --- a/ipinfo/handler.py +++ b/ipinfo/handler.py @@ -5,8 +5,10 @@ import ipaddress import json import os -import requests import sys + +import requests + from .cache.default import DefaultCache from .details import Details from .exceptions import RequestQuotaExceededError @@ -18,35 +20,37 @@ class Handler: and maintains access to cache. """ - API_URL = 'https://ipinfo.io' + API_URL = "https://ipinfo.io" CACHE_MAXSIZE = 4096 CACHE_TTL = 60 * 60 * 24 - COUNTRY_FILE_DEFAULT = 'countries.json' + COUNTRY_FILE_DEFAULT = "countries.json" REQUEST_TIMEOUT_DEFAULT = 2 def __init__(self, access_token=None, **kwargs): """Initialize the Handler object with country name list and the cache initialized.""" self.access_token = access_token - self.countries = self._read_country_names(kwargs.get('countries_file')) + self.countries = self._read_country_names(kwargs.get("countries_file")) - self.request_options = kwargs.get('request_options', {}) - if 'timeout' not in self.request_options: - self.request_options['timeout'] = self.REQUEST_TIMEOUT_DEFAULT + self.request_options = kwargs.get("request_options", {}) + if "timeout" not in self.request_options: + self.request_options["timeout"] = self.REQUEST_TIMEOUT_DEFAULT - if 'cache' in kwargs: - self.cache = kwargs['cache'] + if "cache" in kwargs: + self.cache = kwargs["cache"] else: - cache_options = kwargs.get('cache_options', {}) - maxsize = cache_options.get('maxsize', self.CACHE_MAXSIZE) - ttl = cache_options.get('ttl', self.CACHE_TTL) + cache_options = kwargs.get("cache_options", {}) + maxsize = cache_options.get("maxsize", self.CACHE_MAXSIZE) + ttl = cache_options.get("ttl", self.CACHE_TTL) self.cache = DefaultCache(maxsize, ttl, **cache_options) def getDetails(self, ip_address=None): """Get details for specified IP address as a Details object.""" raw_details = self._requestDetails(ip_address) - raw_details['country_name'] = self.countries.get(raw_details.get('country')) - raw_details['ip_address'] = ipaddress.ip_address(raw_details.get('ip')) - raw_details['latitude'], raw_details['longitude'] = self._read_coords(raw_details.get('loc')) + raw_details["country_name"] = self.countries.get(raw_details.get("country")) + raw_details["ip_address"] = ipaddress.ip_address(raw_details.get("ip")) + raw_details["latitude"], raw_details["longitude"] = self._read_coords( + raw_details.get("loc") + ) return Details(raw_details) def _requestDetails(self, ip_address=None): @@ -54,9 +58,11 @@ def _requestDetails(self, ip_address=None): if ip_address not in self.cache: url = self.API_URL if ip_address: - url += '/' + ip_address + url += "/" + ip_address - response = requests.get(url, headers=self._get_headers(), **self.request_options) + response = requests.get( + url, headers=self._get_headers(), **self.request_options + ) if response.status_code == 429: raise RequestQuotaExceededError() response.raise_for_status() @@ -67,18 +73,20 @@ def _requestDetails(self, ip_address=None): def _get_headers(self): """Built headers for request to IPinfo API.""" headers = { - 'user-agent': 'IPinfoClient/Python{version}/1.0'.format(version=sys.version_info[0]), - 'accept': 'application/json' + "user-agent": "IPinfoClient/Python{version}/1.0".format( + version=sys.version_info[0] + ), + "accept": "application/json", } if self.access_token: - headers['authorization'] = 'Bearer {}'.format(self.access_token) + headers["authorization"] = "Bearer {}".format(self.access_token) return headers def _read_coords(self, location): lat, lon = None, None - coords = tuple(location.split(',')) if location else '' + coords = tuple(location.split(",")) if location else "" if len(coords) == 2 and coords[0] and coords[1]: lat, lon = coords[0], coords[1] return lat, lon @@ -86,7 +94,9 @@ def _read_coords(self, location): def _read_country_names(self, countries_file=None): """Read list of countries from specified country file or default file.""" if not countries_file: - countries_file = os.path.join(os.path.dirname(__file__), self.COUNTRY_FILE_DEFAULT) + countries_file = os.path.join( + os.path.dirname(__file__), self.COUNTRY_FILE_DEFAULT + ) with open(countries_file) as f: countries_json = f.read() diff --git a/requirements.in b/requirements.in index 7d37a91..6880d3b 100644 --- a/requirements.in +++ b/requirements.in @@ -1,7 +1,8 @@ -cachetools==2.1.0 -pip-tools==3.1.0 -pycodestyle==2.4.0 -pytest==3.8.2 -python-dateutil==2.6.1 -pytz==2017.2 +# For app requests>=2.18.4 +cachetools==3.1.1 +pytest==4.5.0 + +# For dev +pip-tools==3.7.0 +black==19.3b0 diff --git a/requirements.txt b/requirements.txt index 1a4e4f9..874fc8f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,23 +2,24 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile --no-index --output-file requirements.txt requirements.in +# pip-compile --no-index --output-file=requirements.txt requirements.in # +appdirs==1.4.3 # via black atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -cachetools==2.1.0 +attrs==19.1.0 # via black, pytest +black==19.3b0 +cachetools==3.1.1 certifi==2019.3.9 # via requests chardet==3.0.4 # via requests -click==7.0 # via pip-tools +click==7.0 # via black, pip-tools idna==2.8 # via requests more-itertools==6.0.0 # via pytest -pip-tools==3.1.0 +pip-tools==3.7.0 pluggy==0.9.0 # via pytest py==1.8.0 # via pytest -pycodestyle==2.4.0 -pytest==3.8.2 -python-dateutil==2.6.1 -pytz==2017.2 +pytest==4.5.0 requests==2.21.0 -six==1.12.0 # via pip-tools, pytest, python-dateutil +six==1.12.0 # via pip-tools, pytest +toml==0.10.0 # via black urllib3==1.24.1 # via requests +wcwidth==0.1.7 # via pytest diff --git a/setup.py b/setup.py index 83be578..804bddb 100644 --- a/setup.py +++ b/setup.py @@ -8,18 +8,17 @@ You can visit our developer docs at https://ipinfo.io/developers. """ -setup(name='ipinfo', - version='1.1.1', - description='Official Python library for IPInfo', - long_description=long_description, - url='https://github.com/ipinfo/python', - author='James Timmins', - author_email='jameshtimmins@gmail.com', - license='Apache License 2.0', - packages=['ipinfo', 'ipinfo.cache'], - install_requires=[ - 'requests', - 'cachetools', - ], - include_package_data=True, - zip_safe=False) +setup( + name="ipinfo", + version="1.1.2", + description="Official Python library for IPInfo", + long_description=long_description, + url="https://github.com/ipinfo/python", + author="James Timmins", + author_email="jameshtimmins@gmail.com", + license="Apache License 2.0", + packages=["ipinfo", "ipinfo.cache"], + install_requires=["requests", "cachetools", "six"], + include_package_data=True, + zip_safe=False, +) diff --git a/tests/details_test.py b/tests/details_test.py index 9026856..dd53c49 100644 --- a/tests/details_test.py +++ b/tests/details_test.py @@ -1,4 +1,5 @@ import pytest + from ipinfo.details import Details diff --git a/tests/handler_test.py b/tests/handler_test.py index 36dfa2f..9368e5b 100644 --- a/tests/handler_test.py +++ b/tests/handler_test.py @@ -1,7 +1,8 @@ +import ipaddress + from ipinfo.cache.default import DefaultCache from ipinfo.details import Details from ipinfo.handler import Handler -import ipaddress def test_init():