Skip to content

Commit 90990a6

Browse files
improve geofeed validation and add lru_cache to API calling functions
1 parent b9613a1 commit 90990a6

4 files changed

Lines changed: 36 additions & 16 deletions

File tree

setup.cfg

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ install_requires =
3232
[options.entry_points]
3333
console_scripts =
3434
ipdata = ipdata.cli:cli
35-
geofeed_validate = ipdata.cli:validate
3635

3736
[options.packages.find]
3837
where = src

src/ipdata/cli.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -248,10 +248,18 @@ def usage(ctx, api_key):
248248
@cli.command(default=True)
249249
@click.argument("resource", required=False, type=str, default="")
250250
@click.option(
251-
"--fields", "-f", required=False, multiple=True, default=[],
251+
"--fields",
252+
"-f",
253+
required=False,
254+
multiple=True,
255+
default=[],
252256
)
253257
@click.option(
254-
"--exclude", "-e", required=False, multiple=True, default=[],
258+
"--exclude",
259+
"-e",
260+
required=False,
261+
multiple=True,
262+
default=[],
255263
)
256264
@click.option("--api-key", "-k", required=False, default=None, help="ipdata API Key")
257265
@click.option(
@@ -295,7 +303,9 @@ def lookup(ctx, resource, fields, api_key, pretty_print, raw, copy, exclude):
295303

296304
# enforce mutual exclusivity of fields and exclude
297305
if exclude and fields:
298-
raise click.ClickException("'--fields / -f' and '--exclude / -e' are mutually exclusive.")
306+
raise click.ClickException(
307+
"'--fields / -f' and '--exclude / -e' are mutually exclusive."
308+
)
299309

300310
# if the user wants to exclude some fields, get all the fields in fields that are not in exclude
301311
if exclude:
@@ -342,11 +352,13 @@ def process(resources, processor, fields):
342352

343353
@cli.command()
344354
@click.argument("input", required=True, type=click.File(mode="r", encoding="utf-8"))
355+
@click.option("--fields", "-f", required=False, multiple=True)
345356
@click.option(
346-
"--fields", "-f", required=False, multiple=True
347-
)
348-
@click.option(
349-
"--exclude", "-e", required=False, multiple=True, default=[],
357+
"--exclude",
358+
"-e",
359+
required=False,
360+
multiple=True,
361+
default=[],
350362
)
351363
@click.option(
352364
"--output", "-o", required=True, type=click.File(mode="w", encoding="utf-8")
@@ -378,13 +390,14 @@ def batch(ctx, input, fields, output, format, exclude):
378390

379391
# enforce mutual exclusivity of fields and exclude
380392
if exclude and fields:
381-
raise click.ClickException("'--fields / -f' and '--exclude / -e' are mutually exclusive.")
393+
raise click.ClickException(
394+
"'--fields / -f' and '--exclude / -e' are mutually exclusive."
395+
)
382396

383397
# if the user wants to exclude some fields, get all the fields in fields that are not in exclude
384398
if exclude:
385399
fields = set(ipdata.valid_fields).difference(set(exclude))
386400

387-
388401
# Prepare requests
389402
ipdata = IPData(ctx.obj["api-key"])
390403
resources = [resource.strip() for resource in input.readlines()]

src/ipdata/geofeeds.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def _download(self):
5050
:raises Exception: if a failure occurs when downloading the geofeed
5151
"""
5252
random_name = hashlib.sha224( str(random.getrandbits(256)).encode('utf-8') ).hexdigest()[:16]
53-
cache_path = f"{pwd}/{self.dir}/{random_name}.csv"
53+
cache_path = f"{self.dir}/{random_name}.csv"
5454

5555
try:
5656
response = requests.get(self.source, timeout=60)
@@ -82,11 +82,10 @@ def entries(self):
8282
if "://" in self.source and not self.cache_path:
8383
self._download()
8484
path = self.cache_path
85-
86-
# if path still contains a url it means the download was not successful in which case we yield nothing!
87-
if "://" in path:
88-
log.warning(f"No entries found {self.source}")
89-
yield
85+
# if path still contains a url it means the download was not successful in which case we yield nothing!
86+
if not path:
87+
log.warning(f"No entries found {self.source}")
88+
return
9089

9190
with open(path) as f:
9291
reader = csv.reader(f)
@@ -99,6 +98,7 @@ def entries(self):
9998
yield GeofeedValidationError(
10099
f"[{entry}] is not a valid geofeed entry. The 'IP Range' and 'Country' fields are required however you must have the requisite minimum number of commas (currently 4) present"
101100
)
101+
return
102102

103103
# instantiate an Entry object
104104
geofeed_entry = Entry(*entry)
@@ -108,12 +108,15 @@ def entries(self):
108108
yield GeofeedValidationError(
109109
f"Duplicate prefixes found {geofeed_entry.ip_range}"
110110
)
111+
return
111112

112113
self.prefixes.add(geofeed_entry.ip_range)
113114

114115
# generate entry
115116
yield geofeed_entry
116117

118+
def __iter__(self):
119+
yield from self.entries()
117120

118121
class Entry(object):
119122
"""

src/ipdata/ipdata.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import requests
2323
import logging
2424
import urllib3
25+
import functools
2526

2627
from requests.adapters import HTTPAdapter, Retry
2728
from rich.logging import RichHandler
@@ -135,6 +136,7 @@ def __init__(
135136
self._session = requests.Session()
136137
self._session.mount("http", adapter)
137138

139+
@functools.lru_cache(maxsize=100)
138140
def _validate_fields(self, fields):
139141
"""
140142
Validates the fields passed in by the user, first ensuring it's a collection. In prior versions 'fields' was a string, however it now needs to be a collection.
@@ -152,6 +154,7 @@ def _validate_fields(self, fields):
152154
f"The field(s) {diff} are not supported. Only {self.valid_fields} are supported."
153155
)
154156

157+
@functools.lru_cache(maxsize=100)
155158
def _validate_ip_address(self, ip):
156159
"""
157160
Checks that 'ip' is a valid IP Address.
@@ -163,6 +166,7 @@ def _validate_ip_address(self, ip):
163166
if request_ip.is_private or request_ip.is_reserved or request_ip.is_multicast:
164167
raise ValueError(f"{ip} is a reserved IP Address")
165168

169+
@functools.lru_cache(maxsize=100)
166170
def lookup(self, resource="", fields=[]):
167171
"""
168172
Makes a GET request to the IPData API for the specified 'resource' and the given 'fields'.
@@ -212,6 +216,7 @@ def lookup(self, resource="", fields=[]):
212216

213217
return data
214218

219+
@functools.lru_cache(maxsize=100)
215220
def bulk(self, resources, fields=[]):
216221
"""
217222
Lookup up to 100 resources in one request. Makes a POST request wth the resources as a JSON array and the specified fields.

0 commit comments

Comments
 (0)