Skip to content

Commit d89fe50

Browse files
committed
Add __repr__ and __eq__ methods to model/record classes
1 parent e965217 commit d89fe50

File tree

4 files changed

+88
-7
lines changed

4 files changed

+88
-7
lines changed

geoip2/models.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
http://dev.maxmind.com/geoip/geoip2/web-services for more details.
1111
1212
"""
13-
# pylint:disable=R0903
13+
# pylint: disable=too-many-instance-attributes,too-few-public-methods
14+
from abc import ABCMeta
15+
1416
import geoip2.records
1517

1618

@@ -66,6 +68,7 @@ class Country(object):
6668
def __init__(self, raw_response, locales=None):
6769
if locales is None:
6870
locales = ['en']
71+
self._locales = locales
6972
self.continent = \
7073
geoip2.records.Continent(locales,
7174
**raw_response.get('continent', {}))
@@ -76,18 +79,27 @@ def __init__(self, raw_response, locales=None):
7679
geoip2.records.Country(locales,
7780
**raw_response.get('registered_country',
7881
{}))
79-
# pylint:disable=bad-continuation
8082
self.represented_country \
8183
= geoip2.records.RepresentedCountry(locales,
8284
**raw_response.get(
83-
'represented_country', {}))
85+
'represented_country', {}))
8486

8587
self.maxmind = \
8688
geoip2.records.MaxMind(**raw_response.get('maxmind', {}))
8789

8890
self.traits = geoip2.records.Traits(**raw_response.get('traits', {}))
8991
self.raw = raw_response
9092

93+
def __eq__(self, other):
94+
return self.__dict__ == other.__dict__
95+
96+
def __repr__(self):
97+
return '{module}.{class_name}({data}, {locales})'.format(
98+
module=self.__module__,
99+
class_name=self.__class__.__name__,
100+
data=self.raw,
101+
locales=self._locales)
102+
91103

92104
class City(Country):
93105

@@ -230,7 +242,24 @@ class Insights(City):
230242
"""
231243

232244

233-
class ConnectionType(object):
245+
class SimpleModel(object):
246+
247+
"""Provides basic methods for non-location models"""
248+
249+
__metaclass__ = ABCMeta
250+
251+
def __eq__(self, other):
252+
return self.__dict__ == other.__dict__
253+
254+
def __repr__(self):
255+
# pylint: disable=no-member
256+
return '{module}.{class_name}({data})'.format(
257+
module=self.__module__,
258+
class_name=self.__class__.__name__,
259+
data=str(self.raw))
260+
261+
262+
class ConnectionType(SimpleModel):
234263

235264
"""Model class for the GeoIP2 Connection-Type
236265
@@ -262,7 +291,7 @@ def __init__(self, raw):
262291
self.raw = raw
263292

264293

265-
class Domain(object):
294+
class Domain(SimpleModel):
266295

267296
"""Model class for the GeoIP2 Domain
268297
@@ -288,7 +317,7 @@ def __init__(self, raw):
288317
self.raw = raw
289318

290319

291-
class ISP(object):
320+
class ISP(SimpleModel):
292321

293322
"""Model class for the GeoIP2 ISP
294323

geoip2/records.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ def __init__(self, **kwargs):
2222
def __setattr__(self, name, value):
2323
raise AttributeError("can't set attribute")
2424

25+
def __eq__(self, other):
26+
return self.__dict__ == other.__dict__
27+
28+
def __repr__(self):
29+
args = ', '.join('%s=%r' % x for x in self.__dict__.items())
30+
return '{module}.{class_name}({data})'.format(
31+
module=self.__module__,
32+
class_name=self.__class__.__name__,
33+
data=args)
34+
2535

2636
class PlaceRecord(Record):
2737

tests/database_test.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
if sys.version_info[0] == 2:
1717
unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
18+
unittest.TestCase.assertRegex = unittest.TestCase.assertRegexpMatches
1819

1920

2021
class TestReader(unittest.TestCase):
@@ -78,6 +79,14 @@ def test_connection_type(self):
7879
record = reader.connection_type(ip_address)
7980
self.assertEqual(record.connection_type, 'Cable/DSL')
8081
self.assertEqual(record.ip_address, ip_address)
82+
83+
self.assertRegex(
84+
str(record), r'ConnectionType\(\{.*Cable/DSL.*\}\)',
85+
'ConnectionType str representation is reasonable')
86+
87+
self.assertEqual(record, eval(repr(record)),
88+
"ConnectionType repr can be eval'd")
89+
8190
reader.close()
8291

8392
def test_domain(self):
@@ -89,6 +98,13 @@ def test_domain(self):
8998
self.assertEqual(record.domain, 'maxmind.com')
9099
self.assertEqual(record.ip_address, ip_address)
91100

101+
self.assertRegex(
102+
str(record), r'Domain\(\{.*maxmind.com.*\}\)',
103+
'Domain str representation is reasonable')
104+
105+
self.assertEqual(record, eval(repr(record)),
106+
"Domain repr can be eval'd")
107+
92108
reader.close()
93109

94110
def test_isp(self):
@@ -104,4 +120,11 @@ def test_isp(self):
104120
self.assertEqual(record.organization, 'Telstra Internet')
105121
self.assertEqual(record.ip_address, ip_address)
106122

123+
self.assertRegex(
124+
str(record), r'ISP\(\{.*Telstra.*\}\)',
125+
'ISP str representation is reasonable')
126+
127+
self.assertEqual(record, eval(repr(record)),
128+
"ISP repr can be eval'd")
129+
107130
reader.close()

tests/models_test.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
if sys.version_info[0] == 2:
1717
unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
18+
unittest.TestCase.assertRegex = unittest.TestCase.assertRegexpMatches
1819

1920

2021
class TestModels(unittest.TestCase):
@@ -58,7 +59,7 @@ def test_insights_full(self):
5859
'geoname_id': 123,
5960
'iso_code': 'HP',
6061
'names': {'en': 'Hennepin'},
61-
}
62+
}
6263
],
6364
'registered_country': {
6465
'geoname_id': 2,
@@ -133,6 +134,21 @@ def test_insights_full(self):
133134
self.assertEqual(model.location.metro_code, 765,
134135
'correct metro_code')
135136

137+
self.assertRegex(
138+
str(
139+
model), r'^geoip2.models.Insights\(\{.*geoname_id.*\}, \[.*en.*\]\)',
140+
'Insights str representation looks reasonable')
141+
142+
self.assertEqual(
143+
model, eval(repr(model)), "Insights repr can be eval'd")
144+
145+
self.assertRegex(
146+
str(model.location), r'^geoip2.records.Location\(.*longitude=.*\)',
147+
'Location str representation is reasonable')
148+
149+
self.assertEqual(model.location, eval(repr(model.location)),
150+
"Location repr can be eval'd")
151+
136152
def test_insights_min(self):
137153
model = geoip2.models.Insights({'traits': {'ip_address': '5.6.7.8'}})
138154
self.assertEqual(type(model), geoip2.models.Insights,
@@ -229,6 +245,9 @@ def test_city_full(self):
229245
'traits is_setellite_provider is True')
230246
self.assertEqual(model.raw, raw, 'raw method produces raw output')
231247

248+
self.assertRegex(
249+
str(model), r'^geoip2.models.City\(\{.*geoname_id.*\}, \[.*en.*\]\)')
250+
232251
def test_unknown_keys(self):
233252
model = geoip2.models.City({'traits': {'ip_address': '1.2.3.4',
234253
'invalid': 'blah'},

0 commit comments

Comments
 (0)