From f881fc91ff7a8186bc4f8fad78a12aa7adee4188 Mon Sep 17 00:00:00 2001 From: Uman Shahzad Date: Thu, 13 Jun 2019 20:34:28 +0500 Subject: [PATCH 1/3] Remove ip_address key. It was causing issues in JSON serialization. It is not necessary to have it, as clients can get it on their own trivially by looking into the 'ip' key for the raw ip string. --- CHANGELOG.md | 13 +++++++++++++ README.md | 16 ---------------- ipinfo/handler.py | 3 --- setup.py | 6 +++--- 4 files changed, 16 insertions(+), 22 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..56a6f65 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# IPInfo Changelog + +## 2.0.0 + +#### Breaking Changes + +- Fix [Issue #8](https://github.com/ipinfo/python/issues/8). + Deleted the `ip_address` key in the details object which was of type [`IPv4Address`](https://docs.python.org/3/library/ipaddress.html). + + This allows serializing the details object (into JSON or something else) without errors by default. + + Users who expected that object type can simply pull the `ip` key instead and turn it into an [`IPv4Address`](https://docs.python.org/3/library/ipaddress.html) + object on their own. diff --git a/README.md b/README.md index b05457a..15860e9 100644 --- a/README.md +++ b/README.md @@ -77,21 +77,6 @@ US United States ``` -### IP Address - -`details.ip_address` will return the an `ipaddress` object from the [Python Standard Library](https://docs.python.org/3/library/ipaddress.html). `details.ip` will still return a string. - -```python ->>> details.ip -104.175.221.247 ->>> type(details.ip) - ->>> details.ip_address -104.175.221.247 ->>> type(details.ip_address) - -``` - #### Longitude and Latitude `details.latitude` and `details.longitude` will return latitude and longitude, respectively, as strings. `details.loc` will still return a composite string of both values. @@ -125,7 +110,6 @@ United States 'country_name': 'United States', 'hostname': 'cpe-104-175-221-247.socal.res.rr.com', 'ip': '104.175.221.247', -'ip_address': IPv4Address('104.175.221.247'), 'loc': '34.0293,-118.3570', 'latitude': '34.0293', 'longitude': '-118.3570', diff --git a/ipinfo/handler.py b/ipinfo/handler.py index a4cc3fa..f133bbb 100644 --- a/ipinfo/handler.py +++ b/ipinfo/handler.py @@ -2,7 +2,6 @@ Main API client handler for fetching data from the IPinfo service. """ -import ipaddress import json import os import sys @@ -30,7 +29,6 @@ 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.request_options = kwargs.get("request_options", {}) if "timeout" not in self.request_options: self.request_options["timeout"] = self.REQUEST_TIMEOUT_DEFAULT @@ -47,7 +45,6 @@ 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") ) diff --git a/setup.py b/setup.py index 804bddb..217aaea 100644 --- a/setup.py +++ b/setup.py @@ -10,12 +10,12 @@ setup( name="ipinfo", - version="1.1.2", + version="2.0.0", description="Official Python library for IPInfo", long_description=long_description, url="https://github.com/ipinfo/python", - author="James Timmins", - author_email="jameshtimmins@gmail.com", + author="IPinfo", + author_email="support@ipinfo.io", license="Apache License 2.0", packages=["ipinfo", "ipinfo.cache"], install_requires=["requests", "cachetools", "six"], From 5e38c95e21201a7605209e9ae8ab2d8423723379 Mon Sep 17 00:00:00 2001 From: Uman Shahzad Date: Thu, 13 Jun 2019 20:56:39 +0500 Subject: [PATCH 2/3] Fix tests, update requirements, test for json serialization --- requirements.txt | 12 ++++--- tests/default_cache_test.py | 8 ++--- tests/details_test.py | 10 +++--- tests/handler_test.py | 66 +++++++++++++++++++++++++++---------- tests/init_test.py | 2 +- 5 files changed, 65 insertions(+), 33 deletions(-) diff --git a/requirements.txt b/requirements.txt index 874fc8f..9078712 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile --no-index --output-file=requirements.txt requirements.in +# pip-compile --no-index # appdirs==1.4.3 # via black atomicwrites==1.3.0 # via pytest @@ -13,13 +13,15 @@ certifi==2019.3.9 # via requests chardet==3.0.4 # via requests click==7.0 # via black, pip-tools idna==2.8 # via requests -more-itertools==6.0.0 # via pytest +importlib-metadata==0.18 # via pluggy +more-itertools==7.0.0 # via pytest pip-tools==3.7.0 -pluggy==0.9.0 # via pytest +pluggy==0.12.0 # via pytest py==1.8.0 # via pytest pytest==4.5.0 -requests==2.21.0 +requests==2.22.0 six==1.12.0 # via pip-tools, pytest toml==0.10.0 # via black -urllib3==1.24.1 # via requests +urllib3==1.25.3 # via requests wcwidth==0.1.7 # via pytest +zipp==0.5.1 # via importlib-metadata diff --git a/tests/default_cache_test.py b/tests/default_cache_test.py index 7827839..e4d1e7f 100644 --- a/tests/default_cache_test.py +++ b/tests/default_cache_test.py @@ -9,13 +9,13 @@ def _get_new_cache(): def test_contains(): cache = _get_new_cache() - cache['foo'] = 'bar' + cache["foo"] = "bar" - assert 'foo' in cache + assert "foo" in cache def test_get(): cache = _get_new_cache() - cache['foo'] = 'bar' + cache["foo"] = "bar" - assert cache['foo'] == 'bar' + assert cache["foo"] == "bar" diff --git a/tests/details_test.py b/tests/details_test.py index dd53c49..d53252e 100644 --- a/tests/details_test.py +++ b/tests/details_test.py @@ -4,25 +4,25 @@ def test_init(): - data = {'foo': 'bar'} + data = {"foo": "bar"} details = Details(data) assert details.details == data def test_getattr_success(): - data = {'foo': 'bar'} + data = {"foo": "bar"} details = Details(data) - assert details.foo == data['foo'] + assert details.foo == data["foo"] def test_getattr_fail(): - data = {'foo': 'bar'} + data = {"foo": "bar"} details = Details(data) with pytest.raises(Exception): details.blah def test_all(): - data = {'foo': 'bar', 'ham': 'eggs'} + data = {"foo": "bar", "ham": "eggs"} details = Details(data) assert details.all == data diff --git a/tests/handler_test.py b/tests/handler_test.py index 9368e5b..6a9769b 100644 --- a/tests/handler_test.py +++ b/tests/handler_test.py @@ -1,4 +1,4 @@ -import ipaddress +import json from ipinfo.cache.default import DefaultCache from ipinfo.details import Details @@ -6,39 +6,69 @@ def test_init(): - token = 'mytesttoken' + token = "mytesttoken" handler = Handler(token) assert handler.access_token == token assert isinstance(handler.cache, DefaultCache) - assert 'US' in handler.countries + assert "US" in handler.countries def test_headers(): - token = 'mytesttoken' + token = "mytesttoken" handler = Handler(token) headers = handler._get_headers() - assert 'user-agent' in headers - assert 'accept' in headers - assert 'authorization' in headers + assert "user-agent" in headers + assert "accept" in headers + assert "authorization" in headers def test_get_details(): + handler = Handler() + fake_details = {"country": "US", "ip": "127.0.0.1", "loc": "12.34,56.78"} + + handler._requestDetails = lambda x: fake_details + + details = handler.getDetails(fake_details["ip"]) + assert isinstance(details, Details) + assert details.country == fake_details["country"] + assert details.country_name == "United States" + assert details.ip == fake_details["ip"] + assert details.loc == fake_details["loc"] + assert details.longitude == "56.78" + assert details.latitude == "12.34" + + +def test_json_serialization(): handler = Handler() fake_details = { - 'country': 'US', - 'ip': '127.0.0.1', - 'loc': '12.34,56.78' + "asn": { + "asn": "AS20001", + "domain": "twcable.com", + "name": "Time Warner Cable Internet LLC", + "route": "104.172.0.0/14", + "type": "isp", + }, + "city": "Los Angeles", + "company": { + "domain": "twcable.com", + "name": "Time Warner Cable Internet LLC", + "type": "isp", + }, + "country": "US", + "country_name": "United States", + "hostname": "cpe-104-175-221-247.socal.res.rr.com", + "ip": "104.175.221.247", + "loc": "34.0293,-118.3570", + "latitude": "34.0293", + "longitude": "-118.3570", + "phone": "323", + "postal": "90016", + "region": "California", } handler._requestDetails = lambda x: fake_details - details = handler.getDetails(fake_details['ip']) + details = handler.getDetails(fake_details["ip"]) assert isinstance(details, Details) - assert details.country == fake_details['country'] - assert details.country_name == 'United States' - assert details.ip == fake_details['ip'] - assert isinstance(details.ip_address, ipaddress.IPv4Address) - assert details.loc == fake_details['loc'] - assert details.longitude == '56.78' - assert details.latitude == '12.34' + assert json.dumps(details.all) diff --git a/tests/init_test.py b/tests/init_test.py index fb20b9b..9a4f020 100644 --- a/tests/init_test.py +++ b/tests/init_test.py @@ -4,4 +4,4 @@ def test_get_handler(): handler = ipinfo.getHandler() - assert(isinstance(handler, Handler)) + assert isinstance(handler, Handler) From e75e5c627f4e907fd95b135c1b3e156d5b8b41b6 Mon Sep 17 00:00:00 2001 From: Uman Shahzad Date: Thu, 13 Jun 2019 21:05:37 +0500 Subject: [PATCH 3/3] Bump user-agent lib version --- ipinfo/handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipinfo/handler.py b/ipinfo/handler.py index f133bbb..07ccceb 100644 --- a/ipinfo/handler.py +++ b/ipinfo/handler.py @@ -70,7 +70,7 @@ 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( + "user-agent": "IPinfoClient/Python{version}/2.0.0".format( version=sys.version_info[0] ), "accept": "application/json",