Skip to content

fix: N+1 OMDb calls in title search with enrich opt-out - #135

Merged
vedaant00 merged 6 commits into
mldsveda:mainfrom
ParthP22:fix/132-opt-out-omdb-calls
Aug 11, 2026
Merged

fix: N+1 OMDb calls in title search with enrich opt-out#135
vedaant00 merged 6 commits into
mldsveda:mainfrom
ParthP22:fix/132-opt-out-omdb-calls

Conversation

@ParthP22

Copy link
Copy Markdown
Contributor

Summary

Closes #132.

Adds an enrich: bool = True parameter to IMDBScraper.scrape / scrape_async, letting callers opt out of the per-hit OMDb detail lookup that title searches previously always performed. Default behavior is unchanged.

Problem

Every title search hit was enriched with a second OMDb call (_get({"i": imdb_id})), with no way to skip it. Call volume was roughly max_pages + max_pages * 10 (OMDb returns ~10 hits/page), which can burn through the free-tier daily quota (1,000 calls/day) quickly and without warning. The docstring mentioned "10 results per page" but never the hidden per-hit cost.

Changes

  • scrape / scrape_async: added enrich: bool = True param, threaded through to _search_by_title / _search_by_title_async.
  • _search_by_title / _search_by_title_async: the per-hit detail call is now skipped when enrich=False, returning the lightweight search row instead (title, year, imdbID, type, poster).
  • Docstrings on both scrape and scrape_async now document the call multiplier (max_pages + max_pages * 10) and the enrich=False opt-out.

Tests

Added to tests/test_scrapers/test_imdb.py:

  • TestIMDBSearchByTitle::test_search_without_enrich_skips_details (sync) — asserts enrich=False results in exactly one HTTP call (get_html.call_count == 1), and that the search still returns data with no errors.
  • TestIMDBSearchByTitleAsync (new class, mirrors the sync class) — covers the async path end to end:
    • test_search_async_enriches_with_details — default (enrich=True) behavior unchanged.
    • test_search_async_no_results — not-found case still works async.
    • test_search_async_without_enrich_skips_details — same one-call assertion as the sync test, via a new _scraper_with_async fixture.

The enrich=False tests queue only one mock response on purpose — if the per-hit lookup ever fired when it shouldn't, the mock would run out of queued responses and error, in addition to the explicit call_count == 1 assertion failing.

_scraper_with_async mocks _async_http with AsyncMock on .get_html (mirroring the existing _scraper_with pattern for _http), since CONTRIBUTING.md's testing principles reference _http specifically but the async path needed its own equivalent.

  • pytest tests/ -v — all passing
  • ruff check src/ — passing

Acceptance criteria (from #132)

  • scrape(query=..., details=False) issues one request per page (no per-hit lookups)
    • Implemented as enrich=False per the issue's suggested naming.
  • Default behavior unchanged (still enriched).
  • Docstring notes the per-hit lookup cost and the opt-out.
  • Applied to both sync and async loops.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses OMDb quota/performance issues in IMDBScraper title searches by adding an opt-out for per-hit detail enrichment, eliminating the previous N+1 request pattern when callers don’t need full movie details.

Changes:

  • Added enrich: bool = True to IMDBScraper.scrape / scrape_async and threaded it through the title-search implementations.
  • Skipped per-hit OMDb detail lookups when enrich=False, returning normalized lightweight search rows instead.
  • Added/expanded tests covering sync + async behavior, including the one-call-per-page expectation for enrich=False.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/pyscrappy/scrapers/imdb.py Adds enrich parameter, gates per-hit detail lookups, and documents the quota/call impact.
tests/test_scrapers/test_imdb.py Adds sync + async tests to ensure enrich=False avoids extra HTTP calls.
Suppressed comments (1)

tests/test_scrapers/test_imdb.py:177

  • The async enrich=False test checks the call count, but it doesn’t validate that the returned item is the lightweight search result (and not a partially-enriched shape). Asserting a couple of expected/absent fields would make the test cover the behavioral contract as well as the N+1 avoidance.
        assert scraper._async_http.get_html.call_count == 1
        assert len(result.data) == 1
        assert result.errors == []

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/pyscrappy/scrapers/imdb.py
Comment thread tests/test_scrapers/test_imdb.py
- ruff: sort imports in test_imdb.py, format imdb.py + test_imdb.py (the
  failing lint check).
- Align the scrape_async docstring with sync: document the
  max_pages + max_pages*10 call multiplier (Copilot).
- Strengthen both enrich=False tests to assert the returned row is the
  lightweight search row: search fields present (title/imdb_id/type),
  detail-only fields absent (genre/director) — _normalise drops missing
  keys, so 'not in' is the correct assertion (Copilot).
@vedaant00

Copy link
Copy Markdown
Collaborator

Thanks @ParthP22, solid fix, enrich=False is threaded through both sync and async and hits every acceptance criterion on #132. I pushed a commit on top: fixed the failing lint (import sort + ruff format), aligned the async docstring with the sync one to document the max_pages + max_pages*10 multiplier, and strengthened both enrich=False tests to assert the row is the lightweight one. Worth noting for the shape check: _normalise drops missing keys entirely rather than setting them to None, so the right assertion is "genre" not in movie (not genre is None). Green now, 479 passed. Nice first contribution.

@vedaant00
vedaant00 merged commit 6a688fd into mldsveda:main Aug 11, 2026
6 checks passed
@ParthP22

Copy link
Copy Markdown
Contributor Author

Thank you for the fixes and the explanation. I understand now where I missed some things, so I will keep them in mind.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IMDBScraper title search makes N+1 OMDb calls with no way to opt out

3 participants