Skip to content

Make aiohttp and requests optional dependencies#177

Open
akx wants to merge 2 commits into
maxmind:mainfrom
akx:split-deps
Open

Make aiohttp and requests optional dependencies#177
akx wants to merge 2 commits into
maxmind:mainfrom
akx:split-deps

Conversation

@akx

@akx akx commented Aug 27, 2024

Copy link
Copy Markdown

This PR makes aiohttp and requests, only required for geoip2.webservice, optional dependencies.

They can be installed separately (many projects will likely have requests anyway), or using the extras provided, i.e.

pip install GeoIP2[aiohttp,requests]

to use the version range specified by requirements.

The Tox configuration is adjusted so tests are run both with and without the libraries installed.

This fixes #101 (makes it possible to install without the webservice bits).
This closes #104 (supersedes it to work with the current packaging and testing infrastructure).

Summary by CodeRabbit

  • New Features
    • Added optional installation extras for web service support: geoip2[aiohttp] and geoip2[requests].
    • Async client now clearly instructs how to install missing async support.
    • User-Agent is now set dynamically to include both the package version and the underlying HTTP client/server details.
  • Documentation
    • Updated installation instructions to mention the new optional extras.
  • Tests
    • Removed import-path workarounds and skip sync/async web service tests when the corresponding optional dependencies aren’t installed.

@oschwald

Copy link
Copy Markdown
Member

As discussed in the issue and other PR, this is a breaking change for web service users. I'd prefer either a solution that allows you to opt-out of the dependencies rather than opt-in or one that breaks the package into several packages where you could depend on the parts that you need with geoip2 remaining a meta packages that includes all of the parts.

@akx

akx commented Aug 28, 2024

Copy link
Copy Markdown
Author

As discussed in the issue and other PR, this is a breaking change for web service users.

Yes, in semver terms this would be a major version bump. (In fact, the reason why I looked into this was that we have a downstream application that has pinned GeoIP to a much older version because the new version would pull in aiohttp and other things we don't want.)

However, practically, chances are that web service users will already have requests or aiohttp installed via other dependencies, or their application's direct dependencies.

I'd prefer either a solution that allows you to opt-out of the dependencies rather than opt-in

Unfortunately that's not possible with Python package extras.

one that breaks the package into several packages where you could depend on the parts that you need with geoip2 remaining a meta packages that includes all of the parts.

In my view, it would be significantly more packaging and infra work for little profit. However, this practically does that already: the core package is geoip2, the "package" that automagically supports sync web services is geoip2[requests], the "package" that automagically supports async web services is geoip2[aiohttp].

@akx
akx force-pushed the split-deps branch 2 times, most recently from 6ac3f9c to 0eebbbd Compare February 10, 2025 14:41
@akx

akx commented Feb 10, 2025

Copy link
Copy Markdown
Author

Rebased on 5.0.1.

@akx
akx force-pushed the split-deps branch 3 times, most recently from e014f68 to 8e25cdf Compare August 20, 2025 07:53
@akx

akx commented Aug 20, 2025

Copy link
Copy Markdown
Author

Rebased.

Looking at the changelog for 5.0.0, this could easily be just another "BREAKING" change for 6.0.0?

@horgh

horgh commented Aug 20, 2025

Copy link
Copy Markdown
Contributor

Thanks for the PR!

Looking at it, I think the approach still has the concerns @oschwald mentioned, in that I think this could break things for existing users as they would need to install new packages in situations where they are not already installing requests/aiohttp. Even when we have breaking changes, we'd prefer to make them as undisruptive as possible.

I understand the current situation isn't ideal. And there may not be a great alternative with the current packaging system. However I think the status quo is preferable until we have a solution that is less disruptive.

@akx

akx commented Aug 21, 2025

Copy link
Copy Markdown
Author

In my opinion, asking users to change a geoip2 requirement to geoip2[requests] or geoip2[aiohttp] depending on what they're doing is not very disruptive 😄

For me and the company I work for, this is a supply chain vettability and possible security issue.
As it is, installing geoip2 for any project pulls in all of these packages:

$ docker run -it python:3.13 pip install geoip2
[...]
aiohappyeyeballs-2.6.1 aiohttp-3.12.15 aiosignal-1.4.0 attrs-25.3.0 certifi-2025.8.3 
charset_normalizer-3.4.3 frozenlist-1.7.0 geoip2-5.1.0 idna-3.10 maxminddb-2.8.2 multidict-6.6.4 
propcache-0.3.2 requests-2.32.5 urllib3-2.5.0 yarl-1.20.1

While installing the library from this PR's HEAD (and in fact, this is what we currently do in production):

$ docker run -it python:3.13 pip install git+https://github.com/akx/GeoIP2-python@split-deps
[...]
Successfully installed geoip2-5.1.0 maxminddb-2.8.2

@horgh

horgh commented Aug 21, 2025

Copy link
Copy Markdown
Contributor

I understand the concern, but we'd prefer to be conservative about potential breakage. The gain here doesn't seem big enough to warrant that.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The package now declares aiohttp and requests as optional extras, guards their imports, reports missing async support, updates user-agent construction, documents installation options, and skips dependent tests when optional libraries are unavailable.

Changes

Optional HTTP dependencies

Layer / File(s) Summary
Optional dependency metadata and installation
pyproject.toml, README.rst
Defines aiohttp and requests extras, installs them in the lint environment, and documents their installation commands.
Guarded imports and user-agent construction
src/geoip2/webservice.py
Makes HTTP imports optional, raises an installation hint when async support lacks aiohttp, and builds sync and async user-agent headers inline.
Import-path cleanup and dependency-aware tests
tests/*_test.py
Removes test path manipulation and skips webservice tests when requests or aiohttp is unavailable.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AsyncClient
  participant geoip2.webservice
  participant aiohttp
  AsyncClient->>geoip2.webservice: Call _session()
  geoip2.webservice->>aiohttp: Check availability and create session
  aiohttp-->>AsyncClient: Return HTTP session
Loading

Suggested reviewers: oschwald, horgh

Poem

A bunny hops through extras bright,
With aiohttp tucked out of sight.
Requests may join when called by name,
Missing paws now raise a claim.
Tests skip softly, paths stay neat—
Optional hops make installs sweet!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR makes aiohttp/requests optional, but it doesn't show the requested urllib3 removal or a clear missing-dependency ImportError for the sync path. Remove urllib3 from the declared dependencies and raise an explicit ImportError with install hints whenever webservice support is used without requests or aiohttp.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: making aiohttp and requests optional dependencies.
Out of Scope Changes check ✅ Passed All changes stay within dependency packaging, webservice fallback behavior, docs, and tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyproject.toml`:
- Around line 106-112: Remove aiohttp and requests from the
[project.dependencies] section, keeping them only under
[project.optional-dependencies] with their existing version constraints. Ensure
the base installation no longer installs either package while the named extras
continue to provide them.

In `@README.rst`:
- Around line 20-21: Update the README installation snippet to quote both
individual extras using shell-safe single quotes and add a third example
demonstrating the combined aiohttp and requests extras in the supported
comma-separated form.

In `@src/geoip2/webservice.py`:
- Around line 36-40: Update Client.__init__ to check whether the optional
requests import is unavailable before calling requests.Session() or
requests.utils.default_user_agent(). Raise an ImportError immediately when
requests is None, while preserving the existing initialization path when the
dependency is installed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dc690fde-006e-4fbc-a7e7-ed77239abf63

📥 Commits

Reviewing files that changed from the base of the PR and between 2a81f07 and 1deb3b4.

📒 Files selected for processing (6)
  • README.rst
  • pyproject.toml
  • src/geoip2/webservice.py
  • tests/database_test.py
  • tests/models_test.py
  • tests/webservice_test.py
💤 Files with no reviewable changes (2)
  • tests/models_test.py
  • tests/database_test.py

Comment thread pyproject.toml Outdated
Comment thread README.rst
Comment thread src/geoip2/webservice.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/webservice_test.py`:
- Line 406: Add focused tests in the relevant webservice test cases to simulate
missing aiohttp and requests imports, then assert that the resulting ImportError
contains the actionable installation guidance. Keep the existing
pytest.importorskip calls in the HTTP integration tests so core-only
environments continue to skip those tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b9ce2d8c-60cc-4147-b50a-3c284c005d59

📥 Commits

Reviewing files that changed from the base of the PR and between 6184e6d and 767efa2.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • README.rst
  • pyproject.toml
  • src/geoip2/webservice.py
  • tests/webservice_test.py

Comment thread tests/webservice_test.py
client: Client

def setUp(self) -> None:
pytest.importorskip("requests")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover missing-dependency behavior instead of only skipping it.

importorskip keeps core-only runs green, but skips every test that could exercise the new actionable ImportError path. Add focused tests that simulate missing aiohttp or requests and assert the installation guidance, while retaining these skips for HTTP integration tests.

Also applies to: 420-420

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/webservice_test.py` at line 406, Add focused tests in the relevant
webservice test cases to simulate missing aiohttp and requests imports, then
assert that the resulting ImportError contains the actionable installation
guidance. Keep the existing pytest.importorskip calls in the HTTP integration
tests so core-only environments continue to skip those tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

Installation of only one component of the package

3 participants