s-librarykit 0.5.1__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/PKG-INFO +1 -1
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/capture_tap.py +41 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/pyproject.toml +1 -1
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_capture_tap.py +24 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/.gitignore +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/.gitlab-ci.yml +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/AGENTS.md +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/CHANGELOG.md +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/LICENSE +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/README.md +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/docs/superpowers/specs/2026-06-24-unified-sessions-design.md +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/__init__.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/__init__.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/_helpers.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/cdp.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/curl_cffi.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/page_evaluate.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/warmup_minter.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/auth.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/blocks.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/__init__.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/_engine.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/camoufox.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/geometry.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/launchers.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/nodriver.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/persistent.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/state.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/bulk.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/checkpoint.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/config_util.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/contract.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/cookie_refresh.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/cookies.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/entities.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/enums.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/errmap.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/errors.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/factory.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/forms.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/graphql.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/limit.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/orchestration/__init__.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/orchestration/health.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/orchestration/onboarding.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/orchestration/session_loader.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/pagination.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/pool.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/ports.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/protocols.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/redaction.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/refresh.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/retry.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/rpc.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/secret_store.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/session_bridge.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/session_paths.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/sessions/__init__.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/sessions/_const.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/sessions/_fsutil.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/stream.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/__init__.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/_helpers.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/http_client.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/permissive.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/rest_client.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/transport_http.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/upload.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/windows.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/conftest.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_antibot.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_auth.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_blocks.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_browser.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_bulk.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_checkpoint.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_config_util.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_cookie_refresh.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_cookies.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_errmap.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_errors.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_factory.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_forms.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_graphql.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_limit.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_limit_contract.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_nodriver_backend.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_orchestration.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_pagination.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_permissive_http.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_pool.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_redaction.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_refresh.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_retry.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_rpc.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_session_bridge.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_session_paths.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_sessions.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_stream.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_transport_client.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_transport_rest.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_transport_sync.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_upload.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_warmup_minter.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_windows.py +0 -0
- {s_librarykit-0.5.1 → s_librarykit-0.5.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: s-librarykit
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: Жирный КОРЕНЬ китов: единая error-иерархия, config/retry/paths-утили, checkpoint/resume, транспорт/auth/sessions — общий код для clikit, adapterkit и доменных пакетов.
|
|
5
5
|
Author: Dmitry
|
|
6
6
|
License: MIT
|
|
@@ -206,6 +206,8 @@ class _PlaywrightAdapter(_BaseAdapter):
|
|
|
206
206
|
self._context = page.context
|
|
207
207
|
# Request-объект → стабильный request_id (для сшивки request/response/failed).
|
|
208
208
|
self._ids: "weakref.WeakKeyDictionary[Any, str]" = weakref.WeakKeyDictionary()
|
|
209
|
+
self._drain_stop: asyncio.Event | None = None
|
|
210
|
+
self._drain_task: Any = None
|
|
209
211
|
|
|
210
212
|
def _id_for(self, request: Any) -> str:
|
|
211
213
|
rid = self._ids.get(request)
|
|
@@ -222,6 +224,10 @@ class _PlaywrightAdapter(_BaseAdapter):
|
|
|
222
224
|
await ctx.expose_binding(self._binding, self._on_binding)
|
|
223
225
|
with contextlib.suppress(Exception):
|
|
224
226
|
await ctx.add_init_script(self._init_script)
|
|
227
|
+
# Дренаж очереди — fallback для движков, где binding не виден из мира init-script
|
|
228
|
+
# (CamouFox/Firefox гонят его в изолированном мире). На chromium очередь пуста.
|
|
229
|
+
self._drain_stop = asyncio.Event()
|
|
230
|
+
self._drain_task = asyncio.ensure_future(self._drain_queue_loop())
|
|
225
231
|
if self._h.on_request is not None:
|
|
226
232
|
ctx.on("request", lambda r: self._spawn(self._handle_request(r)))
|
|
227
233
|
if self._h.on_response is not None:
|
|
@@ -307,7 +313,42 @@ class _PlaywrightAdapter(_BaseAdapter):
|
|
|
307
313
|
return await self._context.storage_state()
|
|
308
314
|
return {"cookies": [], "origins": []}
|
|
309
315
|
|
|
316
|
+
async def _drain_queue_loop(self) -> None:
|
|
317
|
+
"""Периодически дренит globalThis.__captureTraceQueue → on_action (fallback CamouFox)."""
|
|
318
|
+
assert self._drain_stop is not None
|
|
319
|
+
while not self._drain_stop.is_set():
|
|
320
|
+
try:
|
|
321
|
+
await asyncio.wait_for(self._drain_stop.wait(), timeout=0.3)
|
|
322
|
+
break
|
|
323
|
+
except asyncio.TimeoutError:
|
|
324
|
+
pass
|
|
325
|
+
await self._drain_queue_once()
|
|
326
|
+
|
|
327
|
+
async def _drain_queue_once(self) -> None:
|
|
328
|
+
try:
|
|
329
|
+
items = await self._page.evaluate(
|
|
330
|
+
"() => { const q = globalThis.__captureTraceQueue || [];"
|
|
331
|
+
" globalThis.__captureTraceQueue = []; return q; }"
|
|
332
|
+
)
|
|
333
|
+
except Exception:
|
|
334
|
+
return
|
|
335
|
+
for s in items or []:
|
|
336
|
+
try:
|
|
337
|
+
payload = json.loads(s) if isinstance(s, str) else s
|
|
338
|
+
except Exception:
|
|
339
|
+
continue
|
|
340
|
+
await self._emit(self._h.on_action, NormalizedAction(raw=payload))
|
|
341
|
+
|
|
310
342
|
async def detach(self) -> None:
|
|
343
|
+
# Остановить дренаж очереди trace (CamouFox-fallback) + финальный дренаж.
|
|
344
|
+
if self._drain_stop is not None:
|
|
345
|
+
self._drain_stop.set()
|
|
346
|
+
with contextlib.suppress(Exception):
|
|
347
|
+
await self._drain_queue_once()
|
|
348
|
+
if self._drain_task is not None:
|
|
349
|
+
self._drain_task.cancel()
|
|
350
|
+
with contextlib.suppress(Exception, asyncio.CancelledError):
|
|
351
|
+
await self._drain_task
|
|
311
352
|
# Слушатели не снимаем поимённо (Playwright требует ссылку на сам хендлер):
|
|
312
353
|
# контекст всё равно закрывает backend.aclose() сразу после detach. Здесь —
|
|
313
354
|
# только дренаж фоновых задач нормализации, чтобы не потерять последние тела.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "s-librarykit"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.2"
|
|
4
4
|
description = "Жирный КОРЕНЬ китов: единая error-иерархия, config/retry/paths-утили, checkpoint/resume, транспорт/auth/sessions — общий код для clikit, adapterkit и доменных пакетов."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -106,10 +106,15 @@ class _MockContext:
|
|
|
106
106
|
class _MockPage:
|
|
107
107
|
def __init__(self, context):
|
|
108
108
|
self.context = context
|
|
109
|
+
self.queue_items: list[str] = []
|
|
109
110
|
|
|
110
111
|
def on(self, *_a):
|
|
111
112
|
pass
|
|
112
113
|
|
|
114
|
+
async def evaluate(self, _script):
|
|
115
|
+
items, self.queue_items = self.queue_items, []
|
|
116
|
+
return items
|
|
117
|
+
|
|
113
118
|
|
|
114
119
|
class _MockBackend:
|
|
115
120
|
def __init__(self, page):
|
|
@@ -183,3 +188,22 @@ def test_attach_tap_playwright_flow():
|
|
|
183
188
|
assert got_resp[0].status == 200 and got_resp[0].body == b'{"ok":true}'
|
|
184
189
|
# request_id сшит между request и response
|
|
185
190
|
assert got_req[0].request_id == got_resp[0].request_id
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_attach_tap_drain_queue_fallback():
|
|
194
|
+
"""CamouFox-fallback: событие из globalThis.__captureTraceQueue дренится page.evaluate → on_action."""
|
|
195
|
+
ctx = _MockContext()
|
|
196
|
+
page = _MockPage(ctx)
|
|
197
|
+
page.queue_items = ['{"kind":"action","type":"click"}', '{"kind":"mutation"}']
|
|
198
|
+
backend = _MockBackend(page)
|
|
199
|
+
got: list = []
|
|
200
|
+
|
|
201
|
+
async def run():
|
|
202
|
+
handle = await attach_capture_tap(
|
|
203
|
+
backend, on_action=got.append, init_script="/*trace*/", action_binding="__t",
|
|
204
|
+
)
|
|
205
|
+
await handle.detach() # финальный дренаж соберёт очередь
|
|
206
|
+
|
|
207
|
+
asyncio.run(run())
|
|
208
|
+
kinds = {a.raw.get("kind") for a in got}
|
|
209
|
+
assert kinds == {"action", "mutation"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|