Skip to content

Commit 61cc836

Browse files
committed
Factor out BaseClient
to share between Client and AsyncClient. Also, don't compute user_agent on every request.
1 parent 02c196e commit 61cc836

File tree

2 files changed

+95
-56
lines changed

2 files changed

+95
-56
lines changed

geoip2/webservice.py

Lines changed: 94 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -47,58 +47,22 @@
4747
from geoip2.types import IPAddress
4848

4949

50-
class Client:
51-
"""Creates a new client object.
52-
53-
It accepts the following required arguments:
54-
55-
:param account_id: Your MaxMind account ID.
56-
:param license_key: Your MaxMind license key.
57-
58-
Go to https://www.maxmind.com/en/my_license_key to see your MaxMind
59-
account ID and license key.
60-
61-
The following keyword arguments are also accepted:
62-
63-
:param host: The hostname to make a request against. This defaults to
64-
"geoip.maxmind.com". In most cases, you should not need to set this
65-
explicitly.
66-
:param locales: This is list of locale codes. This argument will be
67-
passed on to record classes to use when their name properties are
68-
called. The default value is ['en'].
69-
70-
The order of the locales is significant. When a record class has
71-
multiple names (country, city, etc.), its name property will return
72-
the name in the first locale that has one.
73-
74-
Note that the only locale which is always present in the GeoIP2
75-
data is "en". If you do not include this locale, the name property
76-
may end up returning None even when the record has an English name.
77-
78-
Currently, the valid locale codes are:
79-
80-
* de -- German
81-
* en -- English names may still include accented characters if that is
82-
the accepted spelling in English. In other words, English does not
83-
mean ASCII.
84-
* es -- Spanish
85-
* fr -- French
86-
* ja -- Japanese
87-
* pt-BR -- Brazilian Portuguese
88-
* ru -- Russian
89-
* zh-CN -- Simplified Chinese.
90-
:param timeout: The timeout to use when waiting on the request. This sets
91-
both the connect timeout and the read timeout.
92-
93-
"""
50+
class BaseClient: # pylint: disable=missing-class-docstring
51+
_account_id: str
52+
_host: str
53+
_license_key: str
54+
_locales: List[str]
55+
_timeout: Optional[float]
56+
_user_agent: str
9457

9558
def __init__(
9659
self,
9760
account_id: int,
9861
license_key: str,
99-
host: str = "geoip.maxmind.com",
100-
locales: Optional[List[str]] = None,
101-
timeout: Optional[float] = None,
62+
host: str,
63+
locales: Optional[List[str]],
64+
timeout: Optional[float],
65+
http_user_agent: str,
10266
) -> None:
10367
"""Construct a Client."""
10468
# pylint: disable=too-many-arguments
@@ -114,6 +78,10 @@ def __init__(
11478
self._license_key = license_key
11579
self._base_uri = "https://%s/geoip/v2.1" % host
11680
self._timeout = timeout
81+
self._user_agent = "GeoIP2-Python-Client/%s %s" % (
82+
geoip2.__version__,
83+
http_user_agent,
84+
)
11785

11886
def city(self, ip_address: IPAddress = "me") -> City:
11987
"""Call GeoIP2 Precision City endpoint with the specified IP.
@@ -167,21 +135,14 @@ def _response_for(
167135
response = requests.get(
168136
uri,
169137
auth=(self._account_id, self._license_key),
170-
headers={"Accept": "application/json", "User-Agent": self._user_agent()},
138+
headers={"Accept": "application/json", "User-Agent": self._user_agent},
171139
timeout=self._timeout,
172140
)
173141
if response.status_code != 200:
174142
raise self._exception_for_error(response, uri)
175143
body = self._handle_success(response, uri)
176144
return model_class(body, locales=self._locales)
177145

178-
@staticmethod
179-
def _user_agent() -> str:
180-
return "GeoIP2 Python Client v%s (%s)" % (
181-
geoip2.__version__,
182-
default_user_agent(),
183-
)
184-
185146
@staticmethod
186147
def _handle_success(response: Response, uri: str) -> Any:
187148
try:
@@ -284,3 +245,81 @@ def _exception_for_non_200_status(status: int, uri: str) -> HTTPError:
284245
status,
285246
uri,
286247
)
248+
249+
250+
class Client(BaseClient):
251+
"""A synchronous GeoIP2 client.
252+
253+
It accepts the following required arguments:
254+
255+
:param account_id: Your MaxMind account ID.
256+
:param license_key: Your MaxMind license key.
257+
258+
Go to https://www.maxmind.com/en/my_license_key to see your MaxMind
259+
account ID and license key.
260+
261+
The following keyword arguments are also accepted:
262+
263+
:param host: The hostname to make a request against. This defaults to
264+
"geoip.maxmind.com". In most cases, you should not need to set this
265+
explicitly.
266+
:param locales: This is list of locale codes. This argument will be
267+
passed on to record classes to use when their name properties are
268+
called. The default value is ['en'].
269+
270+
The order of the locales is significant. When a record class has
271+
multiple names (country, city, etc.), its name property will return
272+
the name in the first locale that has one.
273+
274+
Note that the only locale which is always present in the GeoIP2
275+
data is "en". If you do not include this locale, the name property
276+
may end up returning None even when the record has an English name.
277+
278+
Currently, the valid locale codes are:
279+
280+
* de -- German
281+
* en -- English names may still include accented characters if that is
282+
the accepted spelling in English. In other words, English does not
283+
mean ASCII.
284+
* es -- Spanish
285+
* fr -- French
286+
* ja -- Japanese
287+
* pt-BR -- Brazilian Portuguese
288+
* ru -- Russian
289+
* zh-CN -- Simplified Chinese.
290+
:param timeout: The timeout to use when waiting on the request. This sets
291+
both the connect timeout and the read timeout.
292+
293+
"""
294+
295+
def __init__( # pylint: disable=too-many-arguments
296+
self,
297+
account_id: int,
298+
license_key: str,
299+
host: str = "geoip.maxmind.com",
300+
locales: Optional[List[str]] = None,
301+
timeout: Optional[float] = None,
302+
) -> None:
303+
super().__init__(
304+
account_id, license_key, host, locales, timeout, default_user_agent()
305+
)
306+
307+
def _response_for(
308+
self,
309+
path: str,
310+
model_class: Union[Type[Insights], Type[City], Type[Country]],
311+
ip_address: IPAddress,
312+
) -> Union[Country, City, Insights]:
313+
if ip_address != "me":
314+
ip_address = ipaddress.ip_address(ip_address)
315+
uri = "/".join([self._base_uri, path, str(ip_address)])
316+
response = requests.get(
317+
uri,
318+
auth=(self._account_id, self._license_key),
319+
headers={"Accept": "application/json", "User-Agent": self._user_agent},
320+
timeout=self._timeout,
321+
)
322+
if response.status_code != 200:
323+
raise self._exception_for_error(response, uri)
324+
body = self._handle_success(response, uri)
325+
return model_class(body, locales=self._locales)

tests/webservice_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ def test_request(self, mock):
288288
)
289289
self.assertRegex(
290290
request.headers["User-Agent"],
291-
"^GeoIP2 Python Client v",
291+
"^GeoIP2-Python-Client/",
292292
"Correct User-Agent",
293293
)
294294
self.assertEqual(

0 commit comments

Comments
 (0)