From ca14f05862924a189e1597427a355fbaef5ff559 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Wed, 23 Sep 2020 13:18:23 -0700 Subject: [PATCH 1/3] Add is_residential_proxy --- HISTORY.rst | 6 ++++++ README.rst | 2 ++ geoip2/models.py | 9 +++++++++ geoip2/records.py | 11 +++++++++++ tests/data | 2 +- tests/database_test.py | 18 ++++++++++++++++++ tests/models_test.py | 2 ++ 7 files changed, 49 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index eae5c2e5..b3842e60 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,12 @@ History ------- +4.1.0 +++++++++++++++++++ + +* Added the ``is_residential_proxy`` attribute to ``geoip2.model.AnonymousIP`` + and ``geoip2.record.Traits``. + 4.0.2 (2020-07-28) ++++++++++++++++++ diff --git a/README.rst b/README.rst index a8d0595b..80d4dd15 100644 --- a/README.rst +++ b/README.rst @@ -229,6 +229,8 @@ Anonymous IP Database False >>> response.is_public_proxy False + >>> response.is_residential_proxy + False >>> response.is_tor_exit_node True >>> response.ip_address diff --git a/geoip2/models.py b/geoip2/models.py index ab9a6953..ef712ab2 100644 --- a/geoip2/models.py +++ b/geoip2/models.py @@ -396,6 +396,13 @@ class AnonymousIP(SimpleModel): :type: bool + .. attribute:: is_residential_proxy + + This is true if the IP address is on a suspected anonymizing network + and belongs to a residential ISP. + + :type: bool + .. attribute:: is_tor_exit_node This is true if the IP address is a Tor exit node. @@ -421,6 +428,7 @@ class AnonymousIP(SimpleModel): is_anonymous_vpn: bool is_hosting_provider: bool is_public_proxy: bool + is_residential_proxy: bool is_tor_exit_node: bool def __init__(self, raw: Dict[str, bool]) -> None: @@ -429,6 +437,7 @@ def __init__(self, raw: Dict[str, bool]) -> None: self.is_anonymous_vpn = raw.get("is_anonymous_vpn", False) self.is_hosting_provider = raw.get("is_hosting_provider", False) self.is_public_proxy = raw.get("is_public_proxy", False) + self.is_residential_proxy = raw.get("is_residential_proxy", False) self.is_tor_exit_node = raw.get("is_tor_exit_node", False) diff --git a/geoip2/records.py b/geoip2/records.py index ce33676a..c2e42698 100644 --- a/geoip2/records.py +++ b/geoip2/records.py @@ -693,6 +693,14 @@ class Traits(Record): :type: bool + .. attribute:: is_residential_proxy + + This is true if the IP address is on a suspected anonymizing network + and belongs to a residential ISP. + + :type: bool + + .. attribute:: is_satellite_provider This is true if the IP address is from a satellite provider that @@ -798,6 +806,7 @@ class Traits(Record): is_hosting_provider: bool is_legitimate_proxy: bool is_public_proxy: bool + is_residential_proxy: bool is_satellite_provider: bool is_tor_exit_node: bool isp: Optional[str] @@ -821,6 +830,7 @@ def __init__( is_hosting_provider: bool = False, is_legitimate_proxy: bool = False, is_public_proxy: bool = False, + is_residential_proxy: bool = False, is_satellite_provider: bool = False, is_tor_exit_node: bool = False, isp: Optional[str] = None, @@ -843,6 +853,7 @@ def __init__( self.is_hosting_provider = is_hosting_provider self.is_legitimate_proxy = is_legitimate_proxy self.is_public_proxy = is_public_proxy + self.is_residential_proxy = is_residential_proxy self.is_satellite_provider = is_satellite_provider self.is_tor_exit_node = is_tor_exit_node self.isp = isp diff --git a/tests/data b/tests/data index f6ed981c..cbaa463d 160000 --- a/tests/data +++ b/tests/data @@ -1 +1 @@ -Subproject commit f6ed981c23b0eb33d7c07568e2177236252afda6 +Subproject commit cbaa463dc6950ababbf678ca85fb3833b81c76d3 diff --git a/tests/database_test.py b/tests/database_test.py index 0817bb96..4084ad4e 100644 --- a/tests/database_test.py +++ b/tests/database_test.py @@ -66,11 +66,29 @@ def test_anonymous_ip(self) -> None: self.assertEqual(record.is_anonymous_vpn, True) self.assertEqual(record.is_hosting_provider, False) self.assertEqual(record.is_public_proxy, False) + self.assertEqual(record.is_residential_proxy, False) self.assertEqual(record.is_tor_exit_node, False) self.assertEqual(record.ip_address, ip_address) self.assertEqual(record.network, ipaddress.ip_network("1.2.0.0/16")) reader.close() + def test_anonymous_ip_all_set(self) -> None: + reader = geoip2.database.Reader( + "tests/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb" + ) + ip_address = "81.2.69.1" + + record = reader.anonymous_ip(ip_address) + self.assertEqual(record.is_anonymous, True) + self.assertEqual(record.is_anonymous_vpn, True) + self.assertEqual(record.is_hosting_provider, True) + self.assertEqual(record.is_public_proxy, True) + self.assertEqual(record.is_residential_proxy, True) + self.assertEqual(record.is_tor_exit_node, True) + self.assertEqual(record.ip_address, ip_address) + self.assertEqual(record.network, ipaddress.ip_network("81.2.69.0/24")) + reader.close() + def test_asn(self) -> None: reader = geoip2.database.Reader("tests/data/test-data/GeoLite2-ASN-Test.mmdb") diff --git a/tests/models_test.py b/tests/models_test.py index 80e201e2..522a57b0 100644 --- a/tests/models_test.py +++ b/tests/models_test.py @@ -79,6 +79,7 @@ def test_insights_full(self) -> None: "is_anonymous_vpn": True, "is_hosting_provider": True, "is_public_proxy": True, + "is_residential_proxy": True, "is_satellite_provider": True, "is_tor_exit_node": True, "isp": "Comcast", @@ -194,6 +195,7 @@ def test_insights_full(self) -> None: self.assertIs(model.traits.is_anonymous_vpn, True) self.assertIs(model.traits.is_hosting_provider, True) self.assertIs(model.traits.is_public_proxy, True) + self.assertIs(model.traits.is_residential_proxy, True) self.assertIs(model.traits.is_satellite_provider, True) self.assertIs(model.traits.is_tor_exit_node, True) self.assertEqual(model.traits.user_count, 2) From d0ee9c77ae4fc0237e1ecc1f818da5462addf8d8 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Wed, 23 Sep 2020 13:23:38 -0700 Subject: [PATCH 2/3] Use updated mocket import. Closes #94 --- setup.py | 2 +- tests/webservice_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 27a2cb56..2194e8f8 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ include_package_data=True, python_requires=">=3.6", install_requires=requirements, - tests_require=["mocket>=3.8.6"], + tests_require=["mocket>=3.8.9"], test_suite="tests", license=geoip2.__license__, classifiers=[ diff --git a/tests/webservice_test.py b/tests/webservice_test.py index 8c716fb0..8f3e6d17 100644 --- a/tests/webservice_test.py +++ b/tests/webservice_test.py @@ -14,7 +14,7 @@ # httpretty currently doesn't work, but mocket with the compat interface # does. from mocket import Mocket # type: ignore -from mocket.plugins.httpretty import HTTPretty as httpretty, httprettified # type: ignore +from mocket.plugins.httpretty import httpretty, httprettified # type: ignore import geoip2 from geoip2.errors import ( AddressNotFoundError, From d7b3dec5f9d65ca109b19a13b16a8a51a72acab2 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Wed, 23 Sep 2020 13:38:08 -0700 Subject: [PATCH 3/3] Mention that attribute is only in Insights --- geoip2/records.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/geoip2/records.py b/geoip2/records.py index c2e42698..cdfc1758 100644 --- a/geoip2/records.py +++ b/geoip2/records.py @@ -696,7 +696,8 @@ class Traits(Record): .. attribute:: is_residential_proxy This is true if the IP address is on a suspected anonymizing network - and belongs to a residential ISP. + and belongs to a residential ISP. This attribute is only available from + GeoIP2 Precision Insights. :type: bool