Skip to content

Commit 45d433b

Browse files
oschwaldclaude
andcommitted
Add residential sub-object to anonymizer for Insights
The web service now nests a `residential` object inside `ip_address .anonymizer`, sourced from the full-feed GeoIP Residential Proxy database. This is exposed automatically via the geoip2 dependency's new `Anonymizer.residential` attribute (a `geoip2.records .AnonymizerFeed`), so no model changes are needed here. Update the test fixtures and assertions to cover the new field, and note the change and the geoip2 5.3.0 minimum in HISTORY.rst. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 78d7e55 commit 45d433b

5 files changed

Lines changed: 40 additions & 2 deletions

File tree

HISTORY.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ History
2121
``aiohttp.encode_basic_auth()`` instead of the ``aiohttp.BasicAuth`` /
2222
``auth=`` parameter, which are deprecated as of aiohttp 3.14.0. As a result,
2323
the minimum required ``aiohttp`` version is now 3.14.0.
24+
* The ``ip_address.anonymizer`` object may now contain a ``residential``
25+
attribute. This is a ``geoip2.records.AnonymizerFeed`` object sourced from
26+
the GeoIP Residential Proxy database and contains the following fields:
27+
``confidence``, ``network_last_seen``, and ``provider_name``. Because the
28+
residential proxy feed is a superset of what is in the Anonymous Plus
29+
database, ``anonymizer`` may now contain only this attribute. This requires
30+
``geoip2`` 5.3.0 or greater.
2431

2532
3.2.0 (2025-11-20)
2633
++++++++++++++++++

tests/data/factors-response.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@
2828
"is_residential_proxy": true,
2929
"is_tor_exit_node": true,
3030
"network_last_seen": "2025-01-15",
31-
"provider_name": "TestVPN"
31+
"provider_name": "TestVPN",
32+
"residential": {
33+
"confidence": 82,
34+
"network_last_seen": "2026-05-11",
35+
"provider_name": "quickshift"
36+
}
3237
},
3338
"city": {
3439
"confidence": 42,

tests/data/insights-response.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@
2828
"is_residential_proxy": true,
2929
"is_tor_exit_node": true,
3030
"network_last_seen": "2025-01-15",
31-
"provider_name": "TestVPN"
31+
"provider_name": "TestVPN",
32+
"residential": {
33+
"confidence": 82,
34+
"network_last_seen": "2026-05-11",
35+
"provider_name": "quickshift"
36+
}
3237
},
3338
"city": {
3439
"confidence": 42,

tests/test_models.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,11 @@ def test_ip_address(self) -> None:
197197
"is_tor_exit_node": True,
198198
"network_last_seen": "2025-01-15",
199199
"provider_name": "TestVPN",
200+
"residential": {
201+
"confidence": 82,
202+
"network_last_seen": "2026-05-11",
203+
"provider_name": "quickshift",
204+
},
200205
},
201206
traits={
202207
"is_anonymous": True,
@@ -238,6 +243,14 @@ def test_ip_address(self) -> None:
238243
)
239244
self.assertEqual("TestVPN", address.anonymizer.provider_name)
240245

246+
# Test anonymizer.residential attribute
247+
self.assertEqual(82, address.anonymizer.residential.confidence)
248+
self.assertEqual(
249+
datetime.date(2026, 5, 11),
250+
address.anonymizer.residential.network_last_seen,
251+
)
252+
self.assertEqual("quickshift", address.anonymizer.residential.provider_name)
253+
241254
self.assertEqual("ANONYMOUS_IP", address.risk_reasons[0].code)
242255
self.assertEqual(
243256
"The IP address belongs to an anonymous network. "

tests/test_webservice.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,14 @@ def test_200(self) -> None:
236236
self.assertEqual("310", model.ip_address.traits.mobile_country_code)
237237
self.assertEqual("004", model.ip_address.traits.mobile_network_code)
238238
self.assertEqual("ANONYMOUS_IP", model.ip_address.risk_reasons[0].code)
239+
self.assertEqual(
240+
82,
241+
model.ip_address.anonymizer.residential.confidence,
242+
)
243+
self.assertEqual(
244+
"quickshift",
245+
model.ip_address.anonymizer.residential.provider_name,
246+
)
239247

240248
def test_authorization_header(self) -> None:
241249
# Credentials must be sent via the Authorization header rather than the

0 commit comments

Comments
 (0)