6
GeoIP(R) is a registered trademark of MaxMind, LLC of Boston, Massachusetts.
6
GeoIP(R) is a registered trademark of MaxMind, LLC of Boston, Massachusetts.
7
7
8
For IP-based geolocation, this module requires the GeoLite Country and City
8
For IP-based geolocation, this module requires the GeoLite Country and City
9
- datasets, in binary format (CSV will not work!). The datasets may be
9
+ datasets, in binary format (CSV will not work!). The datasets may be
10
downloaded from MaxMind at http://www.maxmind.com/download/geoip/database/.
10
downloaded from MaxMind at http://www.maxmind.com/download/geoip/database/.
11
Grab GeoIP.dat.gz and GeoLiteCity.dat.gz, and unzip them in the directory
11
Grab GeoIP.dat.gz and GeoLiteCity.dat.gz, and unzip them in the directory
12
corresponding to settings.GEOIP_PATH. See the GeoIP docstring and examples
12
corresponding to settings.GEOIP_PATH. See the GeoIP docstring and examples
34
>>> g.lat_lon('salon.com')
34
>>> g.lat_lon('salon.com')
35
(37.789798736572266, -122.39420318603516)
35
(37.789798736572266, -122.39420318603516)
36
>>> g.lon_lat('uh.edu')
36
>>> g.lon_lat('uh.edu')
37
- (-95.415199279785156, 29.77549934387207)
37
+ (-95.415199279785156, 29.77549934387207)
38
>>> g.geos('24.124.1.80').wkt
38
>>> g.geos('24.124.1.80').wkt
39
'POINT (-95.2087020874023438 39.0392990112304688)'
39
'POINT (-95.2087020874023438 39.0392990112304688)'
40
"""
40
"""
45
if not settings .configured : settings .configure ()
45
if not settings .configured : settings .configure ()
46
46
47
# Creating the settings dictionary with any settings, if needed.
47
# Creating the settings dictionary with any settings, if needed.
48
- GEOIP_SETTINGS = dict ((key , getattr (settings , key ))
48
+ GEOIP_SETTINGS = dict ((key , getattr (settings , key ))
49
for key in ('GEOIP_PATH' , 'GEOIP_LIBRARY_PATH' , 'GEOIP_COUNTRY' , 'GEOIP_CITY' )
49
for key in ('GEOIP_PATH' , 'GEOIP_LIBRARY_PATH' , 'GEOIP_COUNTRY' , 'GEOIP_CITY' )
50
if hasattr (settings , key ))
50
if hasattr (settings , key ))
51
lib_path = GEOIP_SETTINGS .get ('GEOIP_LIBRARY_PATH' , None )
51
lib_path = GEOIP_SETTINGS .get ('GEOIP_LIBRARY_PATH' , None )
@@ -83,8 +83,17 @@ class GeoIPRecord(Structure):
83
('postal_code' , c_char_p ),
83
('postal_code' , c_char_p ),
84
('latitude' , c_float ),
84
('latitude' , c_float ),
85
('longitude' , c_float ),
85
('longitude' , c_float ),
86
+ # TODO: In 1.4.6 this changed from `int dma_code;` to
87
+ # `union {int metro_code; int dma_code;};`. Change
88
+ # to a `ctypes.Union` in to accomodate in future when
89
+ # pre-1.4.6 versions are no longer distributed.
86
('dma_code' , c_int ),
90
('dma_code' , c_int ),
87
('area_code' , c_int ),
91
('area_code' , c_int ),
92
+ # TODO: The following structure fields were added in 1.4.3 --
93
+ # uncomment these fields when sure previous versions are no
94
+ # longer distributed by package maintainers.
95
+ #('charset', c_int),
96
+ #('continent_code', c_char_p),
88
]
97
]
89
class GeoIPTag (Structure ): pass
98
class GeoIPTag (Structure ): pass
90
99
@@ -99,9 +108,12 @@ def record_output(func):
99
rec_by_addr = record_output (lgeoip .GeoIP_record_by_addr )
108
rec_by_addr = record_output (lgeoip .GeoIP_record_by_addr )
100
rec_by_name = record_output (lgeoip .GeoIP_record_by_name )
109
rec_by_name = record_output (lgeoip .GeoIP_record_by_name )
101
110
102
- # For opening up GeoIP databases .
111
+ # For opening & closing GeoIP database files .
103
geoip_open = lgeoip .GeoIP_open
112
geoip_open = lgeoip .GeoIP_open
104
geoip_open .restype = DBTYPE
113
geoip_open .restype = DBTYPE
114
+ geoip_close = lgeoip .GeoIP_delete
115
+ geoip_close .argtypes = [DBTYPE ]
116
+ geoip_close .restype = None
105
117
106
# String output routines.
118
# String output routines.
107
def string_output (func ):
119
def string_output (func ):
@@ -136,6 +148,12 @@ class GeoIP(object):
136
GEOIP_CHECK_CACHE = 2
148
GEOIP_CHECK_CACHE = 2
137
GEOIP_INDEX_CACHE = 4
149
GEOIP_INDEX_CACHE = 4
138
cache_options = dict ((opt , None ) for opt in (0 , 1 , 2 , 4 ))
150
cache_options = dict ((opt , None ) for opt in (0 , 1 , 2 , 4 ))
151
+ _city_file = ''
152
+ _country_file = ''
153
+
154
+ # Initially, pointers to GeoIP file references are NULL.
155
+ _city = None
156
+ _country = None
139
157
140
def __init__ (self , path = None , cache = 0 , country = None , city = None ):
158
def __init__ (self , path = None , cache = 0 , country = None , city = None ):
141
"""
159
"""
@@ -174,43 +192,42 @@ def __init__(self, path=None, cache=0, country=None, city=None):
174
if not isinstance (path , basestring ):
192
if not isinstance (path , basestring ):
175
raise TypeError ('Invalid path type: %s' % type (path ).__name__ )
193
raise TypeError ('Invalid path type: %s' % type (path ).__name__ )
176
194
177
- cntry_ptr , city_ptr = (None , None )
178
if os .path .isdir (path ):
195
if os .path .isdir (path ):
179
- # Getting the country and city files using the settings
196
+ # Constructing the GeoIP database filenames using the settings
180
- # dictionary. If no settings are provided, default names
197
+ # dictionary. If the database files for the GeoLite country
181
- # are assigned.
198
+ # and/or city datasets exist, then try and open them.
182
- country = os .path .join (path , country or GEOIP_SETTINGS .get ('GEOIP_COUNTRY' , 'GeoIP.dat' ))
199
+ country_db = os .path .join (path , country or GEOIP_SETTINGS .get ('GEOIP_COUNTRY' , 'GeoIP.dat' ))
183
- city = os .path .join (path , city or GEOIP_SETTINGS .get ('GEOIP_CITY' , 'GeoLiteCity.dat' ))
200
+ if os .path .isfile (country_db ):
201
+ self ._country = geoip_open (country_db , cache )
202
+ self ._country_file = country_db
203
+
204
+ city_db = os .path .join (path , city or GEOIP_SETTINGS .get ('GEOIP_CITY' , 'GeoLiteCity.dat' ))
205
+ if os .path .isfile (city_db ):
206
+ self ._city = geoip_open (city_db , cache )
207
+ self ._city_file = city_db
184
elif os .path .isfile (path ):
208
elif os .path .isfile (path ):
185
# Otherwise, some detective work will be needed to figure
209
# Otherwise, some detective work will be needed to figure
186
# out whether the given database path is for the GeoIP country
210
# out whether the given database path is for the GeoIP country
187
# or city databases.
211
# or city databases.
188
ptr = geoip_open (path , cache )
212
ptr = geoip_open (path , cache )
189
info = geoip_dbinfo (ptr )
213
info = geoip_dbinfo (ptr )
190
if lite_regex .match (info ):
214
if lite_regex .match (info ):
191
- # GeoLite City database.
215
+ # GeoLite City database detected.
192
- city , city_ptr = path , ptr
216
+ self ._city = ptr
217
+ self ._city_file = path
193
elif free_regex .match (info ):
218
elif free_regex .match (info ):
194
- # GeoIP Country database.
219
+ # GeoIP Country database detected.
195
- country , cntry_ptr = path , ptr
220
+ self ._country = ptr
221
+ self ._country_file = path
196
else :
222
else :
197
raise GeoIPException ('Unable to recognize database edition: %s' % info )
223
raise GeoIPException ('Unable to recognize database edition: %s' % info )
198
else :
224
else :
199
raise GeoIPException ('GeoIP path must be a valid file or directory.' )
225
raise GeoIPException ('GeoIP path must be a valid file or directory.' )
200
-
226
+
201
- # `_init_db` does the dirty work.
227
+ def __del__ (self ):
202
- self ._init_db (country , cache , '_country' , cntry_ptr )
228
+ # Cleaning any GeoIP file handles lying around.
203
- self ._init_db (city , cache , '_city' , city_ptr )
229
+ if self ._country : geoip_close (self ._country )
204
-
230
+ if self ._city : geoip_close (self ._city )
205
- def _init_db (self , db_file , cache , attname , ptr = None ):
206
- "Helper routine for setting GeoIP ctypes database properties."
207
- if ptr :
208
- # Pointer already retrieved.
209
- pass
210
- elif os .path .isfile (db_file or '' ):
211
- ptr = geoip_open (db_file , cache )
212
- setattr (self , attname , ptr )
213
- setattr (self , '%s_file' % attname , db_file )
214
231
215
def _check_query (self , query , country = False , city = False , city_or_country = False ):
232
def _check_query (self , query , country = False , city = False , city_or_country = False ):
216
"Helper routine for checking the query and database availability."
233
"Helper routine for checking the query and database availability."
@@ -219,11 +236,11 @@ def _check_query(self, query, country=False, city=False, city_or_country=False):
219
raise TypeError ('GeoIP query must be a string, not type %s' % type (query ).__name__ )
236
raise TypeError ('GeoIP query must be a string, not type %s' % type (query ).__name__ )
220
237
221
# Extra checks for the existence of country and city databases.
238
# Extra checks for the existence of country and city databases.
222
- if city_or_country and self ._country is None and self ._city is None :
239
+ if city_or_country and not ( self ._country or self ._city ) :
223
raise GeoIPException ('Invalid GeoIP country and city data files.' )
240
raise GeoIPException ('Invalid GeoIP country and city data files.' )
224
- elif country and self ._country is None :
241
+ elif country and not self ._country :
225
raise GeoIPException ('Invalid GeoIP country data file: %s' % self ._country_file )
242
raise GeoIPException ('Invalid GeoIP country data file: %s' % self ._country_file )
226
- elif city and self ._city is None :
243
+ elif city and not self ._city :
227
raise GeoIPException ('Invalid GeoIP city data file: %s' % self ._city_file )
244
raise GeoIPException ('Invalid GeoIP city data file: %s' % self ._city_file )
228
245
229
def city (self , query ):
246
def city (self , query ):
@@ -247,7 +264,7 @@ def city(self, query):
247
return dict ((tup [0 ], getattr (record , tup [0 ])) for tup in record ._fields_ )
264
return dict ((tup [0 ], getattr (record , tup [0 ])) for tup in record ._fields_ )
248
else :
265
else :
249
return None
266
return None
250
-
267
+
251
def country_code (self , query ):
268
def country_code (self , query ):
252
"Returns the country code for the given IP Address or FQDN."
269
"Returns the country code for the given IP Address or FQDN."
253
self ._check_query (query , city_or_country = True )
270
self ._check_query (query , city_or_country = True )
@@ -268,12 +285,12 @@ def country_name(self, query):
268
285
269
def country (self , query ):
286
def country (self , query ):
270
"""
287
"""
271
- Returns a dictonary with with the country code and name when given an
288
+ Returns a dictonary with with the country code and name when given an
272
IP address or a Fully Qualified Domain Name (FQDN). For example, both
289
IP address or a Fully Qualified Domain Name (FQDN). For example, both
273
'24.124.1.80' and 'djangoproject.com' are valid parameters.
290
'24.124.1.80' and 'djangoproject.com' are valid parameters.
274
"""
291
"""
275
# Returning the country code and name
292
# Returning the country code and name
276
- return {'country_code' : self .country_code (query ),
293
+ return {'country_code' : self .country_code (query ),
277
'country_name' : self .country_name (query ),
294
'country_name' : self .country_name (query ),
278
}
295
}
279
296
@@ -318,7 +335,7 @@ def city_info(self):
318
ci = geoip_dbinfo (self ._city )
335
ci = geoip_dbinfo (self ._city )
319
return ci
336
return ci
320
city_info = property (city_info )
337
city_info = property (city_info )
321
-
338
+
322
def info (self ):
339
def info (self ):
323
"Returns information about all GeoIP databases in use."
340
"Returns information about all GeoIP databases in use."
324
return 'Country:\n \t %s\n City:\n \t %s' % (self .country_info , self .city_info )
341
return 'Country:\n \t %s\n City:\n \t %s' % (self .country_info , self .city_info )
0 commit comments