Skip to content

Commit ed864d3

Browse files
authored
Merge pull request #49 from ipinfo/uman/vsn-cache-key
Implement versioned cache key.
2 parents e229f52 + ba05c4c commit ed864d3

File tree

6 files changed

+52
-22
lines changed

6 files changed

+52
-22
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# IPInfo Changelog
22

3+
## 4.2.0
4+
5+
- Cache keys are now versioned.
6+
This allows more reliable changes to cached data in the future without
7+
causing confusing incompatibilities. This should be transparent to the user.
8+
This is primarily useful for users with persistent cache implementations.
9+
310
## 4.1.0
411

512
- The SDK version is available via `ipinfo.version` as `SDK_VERSION`.

ipinfo/handler.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
CACHE_TTL,
2222
REQUEST_TIMEOUT_DEFAULT,
2323
BATCH_REQ_TIMEOUT_DEFAULT,
24+
cache_key,
2425
)
2526
from . import handler_utils
2627

@@ -74,8 +75,12 @@ def getDetails(self, ip_address=None, timeout=None):
7475
):
7576
ip_address = ip_address.exploded
7677

77-
if ip_address in self.cache:
78-
return Details(self.cache[ip_address])
78+
# check cache first.
79+
try:
80+
cached_ipaddr = self.cache[cache_key(ip_address)]
81+
return Details(cached_ipaddr)
82+
except KeyError:
83+
pass
7984

8085
# prepare req http opts
8186
req_opts = {**self.request_options}
@@ -95,7 +100,7 @@ def getDetails(self, ip_address=None, timeout=None):
95100

96101
# format & cache
97102
handler_utils.format_details(details, self.countries)
98-
self.cache[ip_address] = details
103+
self.cache[cache_key(ip_address)] = details
99104

100105
return Details(details)
101106

@@ -153,9 +158,10 @@ def getBatchDetails(
153158
):
154159
ip_address = ip_address.exploded
155160

156-
if ip_address in self.cache:
157-
result[ip_address] = self.cache[ip_address]
158-
else:
161+
try:
162+
cached_ipaddr = self.cache[cache_key(ip_address)]
163+
result[ip_address] = cached_ipaddr
164+
except KeyError:
159165
lookup_addresses.append(ip_address)
160166

161167
# all in cache - return early.
@@ -201,7 +207,7 @@ def getBatchDetails(
201207
# fill cache
202208
json_response = response.json()
203209
for ip_address, details in json_response.items():
204-
self.cache[ip_address] = details
210+
self.cache[cache_key(ip_address)] = details
205211

206212
# merge cached results with new lookup
207213
result.update(json_response)

ipinfo/handler_async.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
CACHE_TTL,
2323
REQUEST_TIMEOUT_DEFAULT,
2424
BATCH_REQ_TIMEOUT_DEFAULT,
25+
cache_key,
2526
)
2627
from . import handler_utils
2728

@@ -99,8 +100,12 @@ async def getDetails(self, ip_address=None, timeout=None):
99100
):
100101
ip_address = ip_address.exploded
101102

102-
if ip_address in self.cache:
103-
return Details(self.cache[ip_address])
103+
# check cache first.
104+
try:
105+
cached_ipaddr = self.cache[cache_key(ip_address)]
106+
return Details(cached_ipaddr)
107+
except KeyError:
108+
pass
104109

105110
# not in cache; do http req
106111
url = API_URL
@@ -118,7 +123,7 @@ async def getDetails(self, ip_address=None, timeout=None):
118123

119124
# format & cache
120125
handler_utils.format_details(details, self.countries)
121-
self.cache[ip_address] = details
126+
self.cache[cache_key(ip_address)] = details
122127

123128
return Details(details)
124129

@@ -181,9 +186,10 @@ async def getBatchDetails(
181186
):
182187
ip_address = ip_address.exploded
183188

184-
if ip_address in self.cache:
185-
result[ip_address] = self.cache[ip_address]
186-
else:
189+
try:
190+
cached_ipaddr = self.cache[cache_key(ip_address)]
191+
result[ip_address] = cached_ipaddr
192+
except KeyError:
187193
lookup_addresses.append(ip_address)
188194

189195
# all in cache - return early.
@@ -267,7 +273,7 @@ async def _do_batch_req(
267273
for ip_address, details in json_resp.items():
268274
if isinstance(details, dict):
269275
handler_utils.format_details(details, self.countries)
270-
self.cache[ip_address] = details
276+
self.cache[cache_key(ip_address)] = details
271277

272278
# merge cached results with new lookup
273279
result.update(json_resp)

ipinfo/handler_utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
# The default TTL of the cache in seconds
2525
CACHE_TTL = 60 * 60 * 24
2626

27+
# The current version of the cached data.
28+
# Update this if the data being cached has changed in shape for the same key.
29+
CACHE_KEY_VSN = "1"
30+
2731
# The default request timeout for per-IP requests.
2832
REQUEST_TIMEOUT_DEFAULT = 2
2933

@@ -93,3 +97,10 @@ def return_or_fail(raise_on_fail, e, v):
9397
raise e
9498
else:
9599
return v
100+
101+
102+
def cache_key(k):
103+
"""
104+
Transforms a user-input key into a versioned cache key.
105+
"""
106+
return f"{k}:{CACHE_KEY_VSN}"

ipinfo/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
SDK_VERSION = "4.1.0"
1+
SDK_VERSION = "4.2.0"

requirements.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,34 @@
44
#
55
# pip-compile --no-emit-index-url --no-emit-trusted-host
66
#
7-
aiohttp==3.7.3 # via -r requirements.in
7+
aiohttp==3.7.4.post0 # via -r requirements.in
88
appdirs==1.4.4 # via black
99
async-timeout==3.0.1 # via aiohttp
1010
attrs==20.3.0 # via aiohttp, pytest
1111
black==20.8b1 # via -r requirements.in
1212
cachetools==4.2.0 # via -r requirements.in
1313
certifi==2020.12.5 # via requests
14-
chardet==3.0.4 # via aiohttp, requests
14+
chardet==4.0.0 # via aiohttp, requests
1515
click==7.1.2 # via black, pip-tools
1616
idna==2.10 # via requests, yarl
1717
iniconfig==1.1.1 # via pytest
1818
multidict==5.1.0 # via aiohttp, yarl
1919
mypy-extensions==0.4.3 # via black
20-
packaging==20.8 # via pytest
20+
packaging==20.9 # via pytest
2121
pathspec==0.8.1 # via black
2222
pip-tools==5.4.0 # via -r requirements.in
2323
pluggy==0.13.1 # via pytest
2424
py==1.10.0 # via pytest
2525
pyparsing==2.4.7 # via packaging
2626
pytest-asyncio==0.14.0 # via -r requirements.in
2727
pytest==6.2.1 # via -r requirements.in, pytest-asyncio
28-
regex==2020.11.13 # via black
29-
requests==2.25.0 # via -r requirements.in
28+
regex==2021.4.4 # via black
29+
requests==2.25.1 # via -r requirements.in
3030
six==1.15.0 # via pip-tools
3131
toml==0.10.2 # via black, pytest
32-
typed-ast==1.4.1 # via black
32+
typed-ast==1.4.3 # via black
3333
typing-extensions==3.7.4.3 # via aiohttp, black
34-
urllib3==1.26.2 # via requests
34+
urllib3==1.26.4 # via requests
3535
yarl==1.6.3 # via aiohttp
3636

3737
# The following packages are considered to be unsafe in a requirements file:

0 commit comments

Comments
 (0)