From 0eebbbdcf3ad997024a3689d40e7fddf56a854fd Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 27 Aug 2024 10:27:12 +0300 Subject: [PATCH] Make aiohttp and requests optional dependencies Closes #104 (supersedes it) Fixes #101 --- README.rst | 2 ++ geoip2/webservice.py | 42 ++++++++++++++++++++++++++-------------- pyproject.toml | 8 ++++++-- setup.cfg | 3 +++ tests/webservice_test.py | 2 ++ 5 files changed, 41 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index 11d1f14..3a11e73 100644 --- a/README.rst +++ b/README.rst @@ -17,6 +17,8 @@ To install the ``geoip2`` module, type: .. code-block:: bash $ pip install geoip2 + $ pip install geoip2[aiohttp] # Install aiohttp as well + $ pip install geoip2[requests] # Install requests as well If you are not able to use pip, you may also use easy_install from the source directory: diff --git a/geoip2/webservice.py b/geoip2/webservice.py index 7b1a9e1..ddbf1a8 100644 --- a/geoip2/webservice.py +++ b/geoip2/webservice.py @@ -25,15 +25,25 @@ """ +from __future__ import annotations + import ipaddress import json from collections.abc import Sequence from typing import Any, Optional, Union, cast -import aiohttp -import aiohttp.http -import requests -import requests.utils +try: + import aiohttp + import aiohttp.http +except ImportError: + aiohttp = None # type: ignore[assignment] + +try: + import requests + import requests.utils +except ImportError: + requests = None # type: ignore[assignment] + import geoip2 import geoip2.models @@ -49,14 +59,6 @@ from geoip2.models import City, Country, Insights from geoip2.types import IPAddress -_AIOHTTP_UA = ( - f"GeoIP2-Python-Client/{geoip2.__version__} {aiohttp.http.SERVER_SOFTWARE}" -) - -_REQUEST_UA = ( - f"GeoIP2-Python-Client/{geoip2.__version__} {requests.utils.default_user_agent()}" -) - class BaseClient: # pylint: disable=missing-class-docstring, too-few-public-methods _account_id: str @@ -346,10 +348,19 @@ async def insights(self, ip_address: IPAddress = "me") -> Insights: ) async def _session(self) -> aiohttp.ClientSession: + if aiohttp is None: + raise ImportError( + "aiohttp is required for async mode; install `GeoIP2[aiohttp]`" + ) + if not hasattr(self, "_existing_session"): + user_agent = ( + f"GeoIP2-Python-Client/{geoip2.__version__} " + f"{aiohttp.http.SERVER_SOFTWARE}" + ) self._existing_session = aiohttp.ClientSession( auth=aiohttp.BasicAuth(self._account_id, self._license_key), - headers={"Accept": "application/json", "User-Agent": _AIOHTTP_UA}, + headers={"Accept": "application/json", "User-Agent": user_agent}, timeout=aiohttp.ClientTimeout(total=self._timeout), ) @@ -456,7 +467,10 @@ def __init__( # pylint: disable=too-many-arguments,too-many-positional-argument self._session = requests.Session() self._session.auth = (self._account_id, self._license_key) self._session.headers["Accept"] = "application/json" - self._session.headers["User-Agent"] = _REQUEST_UA + self._session.headers["User-Agent"] = ( + f"GeoIP2-Python-Client/{geoip2.__version__}" + f" {requests.utils.default_user_agent()}" + ) if proxy is None: self._proxies = None else: diff --git a/pyproject.toml b/pyproject.toml index b77899c..3e3f81a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,9 +10,7 @@ authors = [ {name = "Gregory Oschwald", email = "goschwald@maxmind.com"}, ] dependencies = [ - "aiohttp>=3.6.2,<4.0.0", "maxminddb>=2.5.1,<3.0.0", - "requests>=2.24.0,<3.0.0", ] requires-python = ">=3.9" readme = "README.rst" @@ -38,6 +36,12 @@ classifiers = [ test = [ "pytest-httpserver>=1.0.10", ] +aiohttp = [ + "aiohttp>=3.6.2,<4.0.0", +] +requests = [ + "requests>=2.24.0,<3.0.0", +] [tool.ruff.lint] select = ["ALL"] diff --git a/setup.cfg b/setup.cfg index 6f275ef..1997f98 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,3 +45,6 @@ deps = types-requests voluptuous-stubs commands = mypy geoip2 tests +extras = + aiohttp + requests diff --git a/tests/webservice_test.py b/tests/webservice_test.py index 9537657..c0c8d26 100644 --- a/tests/webservice_test.py +++ b/tests/webservice_test.py @@ -396,6 +396,7 @@ class TestClient(TestBaseClient): client: Client def setUp(self) -> None: + pytest.importorskip("requests") self.client_class = Client self.client = Client(42, "abcdef123456") self.client._base_uri = self.httpserver.url_for("/geoip/v2.1") @@ -409,6 +410,7 @@ class TestAsyncClient(TestBaseClient): client: AsyncClient def setUp(self) -> None: + pytest.importorskip("aiohttp") self._loop = asyncio.new_event_loop() self.client_class = AsyncClient self.client = AsyncClient(42, "abcdef123456")