Fail fast with a clear error on event loop reuse instead of hanging

A Stealthii instance binds its lock, browser, and contexts to whichever
asyncio event loop is running the first time it is used. Using it again
from a different loop previously hung forever (the lock/semaphore/
Playwright transport are all bound to the dead first loop and never
wake the waiting coroutine). Detected in rsearch's test suite, where
pytest-asyncio's default per-test-function event loop broke the
module-level Stealthii singleton shared across tests.

Now raises StealthUnavailableError immediately with an actionable
message. README documents the constraint and the pytest-asyncio config
fix (asyncio_default_test_loop_scope = session).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0116i7dYLNZTXNR7Lqf439bV
This commit is contained in:
2026-09-10 01:58:55 +02:00
co-authored by Claude Sonnet 5
parent 23cdad8966
commit 423170e1bb
5 changed files with 105 additions and 61 deletions
+10 -1
View File
@@ -1,7 +1,7 @@
# retoor <retoor@molodetz.nl>
import pytest
from stealthii import Stealthii, StealthResponse
from stealthii import Stealthii, StealthResponse, StealthUnavailableError
from stealthii.client import _ContextSlot
@@ -69,6 +69,15 @@ async def test_stealth_response_shapes_like_aiohttp():
assert r is resp
@pytest.mark.asyncio
async def test_get_lock_raises_clearly_on_event_loop_mismatch():
s = Stealthii()
s._get_lock()
s._loop = object()
with pytest.raises(StealthUnavailableError, match='different asyncio event loop'):
s._get_lock()
def test_context_slot_starts_fresh():
slot = _ContextSlot(context=object())
assert slot.count == 0