Skip to content

Commit 740e941

Browse files
committed
Only compute network when needed
This provides a significant performance boost in the case where it is never used.
1 parent 943089f commit 740e941

File tree

4 files changed

+64
-27
lines changed

4 files changed

+64
-27
lines changed

geoip2/database.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import geoip2
1515
import geoip2.models
1616
import geoip2.errors
17-
from geoip2.compat import compat_ip_network
1817

1918

2019
class Reader(object):
@@ -189,22 +188,19 @@ def _get(self, database_type, ip_address):
189188
if record is None:
190189
raise geoip2.errors.AddressNotFoundError(
191190
"The address %s is not in the database." % ip_address)
192-
193-
network = compat_ip_network('{}/{}'.format(ip_address, prefix_len),
194-
False)
195-
return (record, network)
191+
return (record, prefix_len)
196192

197193
def _model_for(self, model_class, types, ip_address):
198-
(record, network) = self._get(types, ip_address)
194+
(record, prefix_len) = self._get(types, ip_address)
199195
traits = record.setdefault('traits', {})
200196
traits['ip_address'] = ip_address
201-
traits['network'] = network
197+
traits['prefix_len'] = prefix_len
202198
return model_class(record, locales=self._locales)
203199

204200
def _flat_model_for(self, model_class, types, ip_address):
205-
(record, network) = self._get(types, ip_address)
201+
(record, prefix_len) = self._get(types, ip_address)
206202
record['ip_address'] = ip_address
207-
record['network'] = network
203+
record['prefix_len'] = prefix_len
208204
return model_class(record)
209205

210206
def metadata(self):

geoip2/models.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
1212
"""
1313
# pylint: disable=too-many-instance-attributes,too-few-public-methods
14+
import ipaddress
1415
from abc import ABCMeta
1516

1617
import geoip2.records
18+
from geoip2.compat import compat_ip_network
1719
from geoip2.mixins import SimpleEquality
1820

1921

@@ -306,9 +308,10 @@ class SimpleModel(SimpleEquality):
306308
__metaclass__ = ABCMeta
307309

308310
def __init__(self, raw):
309-
self.ip_address = raw.get('ip_address')
310-
self.network = raw.get('network')
311311
self.raw = raw
312+
self._network = None
313+
self._prefix_len = raw.get('prefix_len')
314+
self.ip_address = raw.get('ip_address')
312315

313316
def __repr__(self):
314317
# pylint: disable=no-member
@@ -317,6 +320,24 @@ def __repr__(self):
317320
class_name=self.__class__.__name__,
318321
data=str(self.raw))
319322

323+
@property
324+
def network(self):
325+
"""The network for the record"""
326+
# This code is duplicated for performance reasons
327+
# pylint: disable=duplicate-code
328+
network = self._network
329+
if isinstance(network, (ipaddress.IPv4Network, ipaddress.IPv6Network)):
330+
return network
331+
332+
ip_address = self.ip_address
333+
prefix_len = self._prefix_len
334+
if ip_address is None or prefix_len is None:
335+
return None
336+
network = compat_ip_network("{}/{}".format(ip_address, prefix_len),
337+
False)
338+
self._network = network
339+
return network
340+
320341

321342
class AnonymousIP(SimpleModel):
322343
"""Model class for the GeoIP2 Anonymous IP.

geoip2/records.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -732,12 +732,9 @@ def __init__(self,
732732
ip_address=None,
733733
network=None,
734734
organization=None,
735+
prefix_len=None,
735736
user_type=None,
736737
**_):
737-
738-
if network is not None and not isinstance(
739-
network, (ipaddress.IPv4Network, ipaddress.IPv6Network)):
740-
network = compat_ip_network(network)
741738
self.autonomous_system_number = autonomous_system_number
742739
self.autonomous_system_organization = autonomous_system_organization
743740
self.connection_type = connection_type
@@ -751,7 +748,27 @@ def __init__(self,
751748
self.is_satellite_provider = is_satellite_provider
752749
self.is_tor_exit_node = is_tor_exit_node
753750
self.isp = isp
754-
self.ip_address = ip_address
755-
self.network = network
756751
self.organization = organization
757752
self.user_type = user_type
753+
self.ip_address = ip_address
754+
self._network = network
755+
self._prefix_len = prefix_len
756+
757+
# This code is duplicated for performance reasons
758+
# pylint: disable=duplicate-code
759+
@property
760+
def network(self):
761+
"""The network for the record"""
762+
network = self._network
763+
if isinstance(network, (ipaddress.IPv4Network, ipaddress.IPv6Network)):
764+
return network
765+
766+
if network is None:
767+
ip_address = self.ip_address
768+
prefix_len = self._prefix_len
769+
if ip_address is None or prefix_len is None:
770+
return None
771+
network = "{}/{}".format(ip_address, prefix_len)
772+
network = compat_ip_network(network, False)
773+
self._network = network
774+
return network

tests/database_test.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ def test_asn(self):
8686

8787
ip_address = '1.128.0.0'
8888
record = reader.asn(ip_address)
89+
90+
self.assertEqual(record, eval(repr(record)), "ASN repr can be eval'd")
91+
8992
self.assertEqual(record.autonomous_system_number, 1221)
9093
self.assertEqual(record.autonomous_system_organization,
9194
'Telstra Pty Ltd')
@@ -95,8 +98,6 @@ def test_asn(self):
9598
self.assertRegex(str(record), r'geoip2.models.ASN\(.*1\.128\.0\.0.*\)',
9699
'str representation is correct')
97100

98-
self.assertEqual(record, eval(repr(record)), "ASN repr can be eval'd")
99-
100101
reader.close()
101102

102103
def test_city(self):
@@ -119,16 +120,17 @@ def test_connection_type(self):
119120
ip_address = '1.0.1.0'
120121

121122
record = reader.connection_type(ip_address)
123+
124+
self.assertEqual(record, eval(repr(record)),
125+
"ConnectionType repr can be eval'd")
126+
122127
self.assertEqual(record.connection_type, 'Cable/DSL')
123128
self.assertEqual(record.ip_address, ip_address)
124129
self.assertEqual(record.network, ipaddress.ip_network('1.0.1.0/24'))
125130

126131
self.assertRegex(str(record), r'ConnectionType\(\{.*Cable/DSL.*\}\)',
127132
'ConnectionType str representation is reasonable')
128133

129-
self.assertEqual(record, eval(repr(record)),
130-
"ConnectionType repr can be eval'd")
131-
132134
reader.close()
133135

134136
def test_country(self):
@@ -149,16 +151,17 @@ def test_domain(self):
149151

150152
ip_address = '1.2.0.0'
151153
record = reader.domain(ip_address)
154+
155+
self.assertEqual(record, eval(repr(record)),
156+
"Domain repr can be eval'd")
157+
152158
self.assertEqual(record.domain, 'maxmind.com')
153159
self.assertEqual(record.ip_address, ip_address)
154160
self.assertEqual(record.network, ipaddress.ip_network('1.2.0.0/16'))
155161

156162
self.assertRegex(str(record), r'Domain\(\{.*maxmind.com.*\}\)',
157163
'Domain str representation is reasonable')
158164

159-
self.assertEqual(record, eval(repr(record)),
160-
"Domain repr can be eval'd")
161-
162165
reader.close()
163166

164167
def test_enterprise(self):
@@ -185,6 +188,8 @@ def test_isp(self):
185188

186189
ip_address = '1.128.0.0'
187190
record = reader.isp(ip_address)
191+
self.assertEqual(record, eval(repr(record)), "ISP repr can be eval'd")
192+
188193
self.assertEqual(record.autonomous_system_number, 1221)
189194
self.assertEqual(record.autonomous_system_organization,
190195
'Telstra Pty Ltd')
@@ -196,8 +201,6 @@ def test_isp(self):
196201
self.assertRegex(str(record), r'ISP\(\{.*Telstra.*\}\)',
197202
'ISP str representation is reasonable')
198203

199-
self.assertEqual(record, eval(repr(record)), "ISP repr can be eval'd")
200-
201204
reader.close()
202205

203206
def test_context_manager(self):

0 commit comments

Comments
 (0)