Skip to content

fix(services/earnings): resolve google.cloud.storage via importlib so sys.modules stubs hold#111

Merged
helloiamvu merged 1 commit into
mainfrom
fix/services-earnings-test-isolation
Jul 9, 2026
Merged

fix(services/earnings): resolve google.cloud.storage via importlib so sys.modules stubs hold#111
helloiamvu merged 1 commit into
mainfrom
fix/services-earnings-test-isolation

Conversation

@helloiamvu

Copy link
Copy Markdown
Member

Root cause

google.cloud is a PEP-420 namespace package. The first real import of google.cloud.storage anywhere in a process (in dev venvs: gcsfs, pulled by the [satellite] extra, imports it transitively) binds it as an attribute on the parent package. From then on, from google.cloud import storage reads that bound attribute and never consults sys.modules — silently bypassing the test fakes installed via monkeypatch.setitem(sys.modules, "google.cloud.storage", ...). Result: 12 tests in services/earnings/tests/ fail in any venv with the satellite deps installed (pass in isolation; CI green only because its minimal env never imports the real module).

Fix

_load_gcs_storage() helper in services/earnings/jobs/{stt,capture}.py resolving via importlib.import_module("google.cloud.storage"), which consults sys.modules first — injected fakes win regardless of import order; byte-identical behavior in production (same real module object). 3 import sites converted; +47/−4, services/earnings/jobs/ only.

Evidence

  • Before (extras venv, py3.12): full pytest -m "not live" -q → 12 FAILED; same tests pass in isolation. Deterministic repro: pre-import gcsfs, run the 12.
  • After (same env, same command): exit 0, 0 FAILED; the 12 pass with gcsfs pre-imported.
  • No-extras: uv sync + pytest services/earnings/tests -m "not live" → 170 passed, 9 skipped.

Pre-existing on main (reproducible at 1ce7aab) — unrelated to the Phase 30 branch.

🤖 Generated with Claude Code

… sys.modules stubs hold

The jobs' lazy `from google.cloud import storage` sites were fragile against
namespace-package attribute binding: google.cloud is a PEP-420 namespace
package, and once the REAL google.cloud.storage submodule is imported anywhere
in the process (e.g. transitively via gcsfs under the [satellite] extra),
Python binds it as an attribute on the google.cloud package object. From then
on `from google.cloud import storage` reads that bound attribute and never
consults sys.modules — silently bypassing the tests'
`monkeypatch.setitem(sys.modules, "google.cloud.storage", fake)` and reaching
the real storage.Client() (12 failures in test_jobs_entrypoints.py +
test_stt_handoff_delete.py in any venv where gcsfs & co. are installed;
green in CI / fresh no-extras venvs where nothing pre-imports the real
submodule).

Fix: resolve the module through importlib.import_module("google.cloud.storage")
(a documented _load_gcs_storage helper in stt.py and capture.py), which
consults sys.modules FIRST — the injected fake wins regardless of import
order, and runtime behavior is byte-identical when no stub is present (same
real module object). google.cloud.pubsub_v1 sites are untouched: no installed
dep pre-imports the real pubsub submodule, so they are not affected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@helloiamvu helloiamvu requested a review from Tarabcak July 9, 2026 00:51
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Parity ticket gate: PASSED

parity-ticket-check: PR does not touch parity-trigger surface; gate skipped.

See CROSS-SDK-SYNC.md §2 for the workflow.

@helloiamvu helloiamvu merged commit 250102a into main Jul 9, 2026
11 checks passed
@helloiamvu helloiamvu deleted the fix/services-earnings-test-isolation branch July 9, 2026 01:09
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.

1 participant