11"""The database reader for MaxMind MMDB files."""
22
3+ from __future__ import annotations
4+
35import inspect
4- import os
5- from collections .abc import Sequence
6- from typing import IO , Any , AnyStr , Optional , Union , cast
6+ from typing import IO , TYPE_CHECKING , AnyStr , cast
77
88import maxminddb
99from maxminddb import (
1313 MODE_MEMORY ,
1414 MODE_MMAP ,
1515 MODE_MMAP_EXT ,
16+ InvalidDatabaseError ,
1617)
1718
1819import geoip2
1920import geoip2 .errors
2021import geoip2 .models
21- from geoip2 .models import (
22- ASN ,
23- ISP ,
24- AnonymousIP ,
25- AnonymousPlus ,
26- City ,
27- ConnectionType ,
28- Country ,
29- Domain ,
30- Enterprise ,
31- )
32- from geoip2 .types import IPAddress
22+
23+ if TYPE_CHECKING :
24+ import os
25+ from collections .abc import Sequence
26+
27+ from typing_extensions import Self
28+
29+ from geoip2 .models import (
30+ ASN ,
31+ ISP ,
32+ AnonymousIP ,
33+ AnonymousPlus ,
34+ City ,
35+ ConnectionType ,
36+ Country ,
37+ Domain ,
38+ Enterprise ,
39+ )
40+ from geoip2 .types import IPAddress
3341
3442__all__ = [
3543 "MODE_AUTO" ,
@@ -67,8 +75,8 @@ class Reader:
6775
6876 def __init__ (
6977 self ,
70- fileish : Union [ AnyStr , int , os .PathLike , IO ] ,
71- locales : Optional [ Sequence [str ]] = None ,
78+ fileish : AnyStr | int | os .PathLike | IO ,
79+ locales : Sequence [str ] | None = None ,
7280 mode : int = MODE_AUTO ,
7381 ) -> None :
7482 """Create GeoIP2 Reader.
@@ -117,10 +125,10 @@ def __init__(
117125 self ._db_type = self ._db_reader .metadata ().database_type
118126 self ._locales = locales
119127
120- def __enter__ (self ) -> "Reader" :
128+ def __enter__ (self ) -> Self :
121129 return self
122130
123- def __exit__ (self , exc_type : None , exc_value : None , traceback : None ) -> None :
131+ def __exit__ (self , exc_type : object , exc_value : object , traceback : object ) -> None :
124132 self .close ()
125133
126134 def country (self , ip_address : IPAddress ) -> Country :
@@ -132,7 +140,7 @@ def country(self, ip_address: IPAddress) -> Country:
132140
133141 """
134142 return cast (
135- Country ,
143+ " Country" ,
136144 self ._model_for (geoip2 .models .Country , "Country" , ip_address ),
137145 )
138146
@@ -144,7 +152,7 @@ def city(self, ip_address: IPAddress) -> City:
144152 :returns: :py:class:`geoip2.models.City` object
145153
146154 """
147- return cast (City , self ._model_for (geoip2 .models .City , "City" , ip_address ))
155+ return cast (" City" , self ._model_for (geoip2 .models .City , "City" , ip_address ))
148156
149157 def anonymous_ip (self , ip_address : IPAddress ) -> AnonymousIP :
150158 """Get the AnonymousIP object for the IP address.
@@ -155,7 +163,7 @@ def anonymous_ip(self, ip_address: IPAddress) -> AnonymousIP:
155163
156164 """
157165 return cast (
158- AnonymousIP ,
166+ " AnonymousIP" ,
159167 self ._flat_model_for (
160168 geoip2 .models .AnonymousIP ,
161169 "GeoIP2-Anonymous-IP" ,
@@ -172,7 +180,7 @@ def anonymous_plus(self, ip_address: IPAddress) -> AnonymousPlus:
172180
173181 """
174182 return cast (
175- AnonymousPlus ,
183+ " AnonymousPlus" ,
176184 self ._flat_model_for (
177185 geoip2 .models .AnonymousPlus ,
178186 "GeoIP-Anonymous-Plus" ,
@@ -189,7 +197,7 @@ def asn(self, ip_address: IPAddress) -> ASN:
189197
190198 """
191199 return cast (
192- ASN ,
200+ " ASN" ,
193201 self ._flat_model_for (geoip2 .models .ASN , "GeoLite2-ASN" , ip_address ),
194202 )
195203
@@ -202,7 +210,7 @@ def connection_type(self, ip_address: IPAddress) -> ConnectionType:
202210
203211 """
204212 return cast (
205- ConnectionType ,
213+ " ConnectionType" ,
206214 self ._flat_model_for (
207215 geoip2 .models .ConnectionType ,
208216 "GeoIP2-Connection-Type" ,
@@ -219,7 +227,7 @@ def domain(self, ip_address: IPAddress) -> Domain:
219227
220228 """
221229 return cast (
222- Domain ,
230+ " Domain" ,
223231 self ._flat_model_for (geoip2 .models .Domain , "GeoIP2-Domain" , ip_address ),
224232 )
225233
@@ -232,7 +240,7 @@ def enterprise(self, ip_address: IPAddress) -> Enterprise:
232240
233241 """
234242 return cast (
235- Enterprise ,
243+ " Enterprise" ,
236244 self ._model_for (geoip2 .models .Enterprise , "Enterprise" , ip_address ),
237245 )
238246
@@ -245,31 +253,38 @@ def isp(self, ip_address: IPAddress) -> ISP:
245253
246254 """
247255 return cast (
248- ISP ,
256+ " ISP" ,
249257 self ._flat_model_for (geoip2 .models .ISP , "GeoIP2-ISP" , ip_address ),
250258 )
251259
252- def _get (self , database_type : str , ip_address : IPAddress ) -> Any :
260+ def _get (self , database_type : str , ip_address : IPAddress ) -> tuple [ dict , int ] :
253261 if database_type not in self ._db_type :
254262 caller = inspect .stack ()[2 ][3 ]
263+ msg = (
264+ f"The { caller } method cannot be used with the { self ._db_type } database"
265+ )
255266 raise TypeError (
256- f"The { caller } method cannot be used with the { self . _db_type } database" ,
267+ msg ,
257268 )
258269 (record , prefix_len ) = self ._db_reader .get_with_prefix_len (ip_address )
259270 if record is None :
271+ msg = f"The address { ip_address } is not in the database."
260272 raise geoip2 .errors .AddressNotFoundError (
261- f"The address { ip_address } is not in the database." ,
273+ msg ,
262274 str (ip_address ),
263275 prefix_len ,
264276 )
277+ if not isinstance (record , dict ):
278+ msg = f"Expected record to be a dict but was f{ type (record )} "
279+ raise InvalidDatabaseError (msg )
265280 return record , prefix_len
266281
267282 def _model_for (
268283 self ,
269- model_class : Union [ type [Country ], type [ Enterprise ], type [ City ] ],
284+ model_class : type [City | Country | Enterprise ],
270285 types : str ,
271286 ip_address : IPAddress ,
272- ) -> Union [ Country , Enterprise , City ] :
287+ ) -> City | Country | Enterprise :
273288 (record , prefix_len ) = self ._get (types , ip_address )
274289 return model_class (
275290 self ._locales ,
@@ -280,28 +295,22 @@ def _model_for(
280295
281296 def _flat_model_for (
282297 self ,
283- model_class : Union [
284- type [Domain ],
285- type [ISP ],
286- type [ConnectionType ],
287- type [ASN ],
288- type [AnonymousIP ],
289- ],
298+ model_class : type [Domain | ISP | ConnectionType | ASN | AnonymousIP ],
290299 types : str ,
291300 ip_address : IPAddress ,
292- ) -> Union [ ConnectionType , ISP , AnonymousIP , Domain , ASN ] :
301+ ) -> ConnectionType | ISP | AnonymousIP | Domain | ASN :
293302 (record , prefix_len ) = self ._get (types , ip_address )
294303 return model_class (ip_address , prefix_len = prefix_len , ** record )
295304
296305 def metadata (
297306 self ,
298307 ) -> maxminddb .reader .Metadata :
299- """The metadata for the open database.
308+ """Get the metadata for the open database.
300309
301310 :returns: :py:class:`maxminddb.reader.Metadata` object
302311 """
303312 return self ._db_reader .metadata ()
304313
305314 def close (self ) -> None :
306- """Closes the GeoIP2 database."""
315+ """Close the GeoIP2 database."""
307316 self ._db_reader .close ()
0 commit comments