Compare commits
No commits in common. "ecec9ff6d7a9474b9d3d5159abcec24b2f986a8a" and "7ebeddc2bc85eb88c7c81ae36e7c1aa52baabcb6" have entirely different histories.
ecec9ff6d7
...
7ebeddc2bc
3 changed files with 9 additions and 102 deletions
|
|
@ -1,83 +0,0 @@
|
||||||
"""Regression-тест для issue #2330 (Group F2 эпика #2277 scraper_kit migration).
|
|
||||||
|
|
||||||
Баг: `scraper_kit/providers/avito/detail.py::build_warmed_session()` строил detail-
|
|
||||||
сессию через `_build_detail_session()` БЕЗ параметра `config` вообще — в отличие от
|
|
||||||
`fetch_detail()` в том же файле, у которой `config=` есть и штатно пробрасывает
|
|
||||||
sticky-прокси в `_build_detail_session(config)`. Т.к. kit-`_build_detail_session` (в
|
|
||||||
отличие от legacy `app/services/scrapers/avito_detail.py`, который читает
|
|
||||||
`settings.scraper_proxy_url` напрямую) читает прокси ТОЛЬКО из инжектируемого
|
|
||||||
`config.scraper_proxy_url`, отсутствие параметра означало, что `build_warmed_session`
|
|
||||||
физически не могло получить прокси ни при каких условиях. Сегодняшний единственный
|
|
||||||
caller (`avito_detail_backfill.py`) всё ещё импортирует эту функцию из legacy-модуля,
|
|
||||||
так что прод-путь этим фиксом не затронут — это advance-prep: как только caller
|
|
||||||
переключится на kit (avito_detail_backfill_use_curl=True — прод-дефолт режим), без
|
|
||||||
этого фикса он тихо терял бы sticky backconnect-proxy на warm-batch операциях. См.
|
|
||||||
`test_scraper_kit_group_c_backfill_kit_parity.py` (issue #2310), который явно
|
|
||||||
документирует этот баг как flagged follow-up.
|
|
||||||
|
|
||||||
Этот файл доказывает РАЗНИЦУ в поведении, а не просто "функция не падает":
|
|
||||||
1. `test_build_warmed_session_signature_now_accepts_config` — до фикса вызов
|
|
||||||
`build_warmed_session(config=...)` был бы `TypeError` (функция принимала 0
|
|
||||||
параметров). Сигнатура теперь содержит `config`.
|
|
||||||
2. `test_build_warmed_session_with_config_propagates_real_proxy_to_session` —
|
|
||||||
реальный (не замоканный) `curl_cffi.requests.AsyncSession`, построенный
|
|
||||||
`build_warmed_session(config=...)`, содержит РЕАЛЬНЫЙ `proxy_url` из config в
|
|
||||||
`session.proxies` — то же самое поведение, что и `fetch_detail(config=...)`.
|
|
||||||
3. `test_build_warmed_session_without_config_has_no_proxy` — вызов без `config`
|
|
||||||
(дефолт `None`, как было ДО фикса единственно возможное поведение) сессия
|
|
||||||
остаётся без прокси — документирует, что caller обязан явно передать `config`,
|
|
||||||
иначе прокси молча теряется (это и есть остающийся риск в
|
|
||||||
`app/tasks/avito_detail_backfill.py`, отдельный будущий шаг вне scope #2330).
|
|
||||||
|
|
||||||
`warm_up_session` патчится no-op'ом — тест проверяет ТОЛЬКО построение сессии
|
|
||||||
(`_build_detail_session`), не сетевой прогрев.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import inspect
|
|
||||||
from types import SimpleNamespace
|
|
||||||
from unittest.mock import AsyncMock, patch
|
|
||||||
|
|
||||||
from scraper_kit.providers.avito import detail as kit_detail
|
|
||||||
from scraper_kit.providers.avito.detail import build_warmed_session
|
|
||||||
|
|
||||||
|
|
||||||
def _fake_config(proxy_url: str | None) -> SimpleNamespace:
|
|
||||||
"""Минимальный duck-typed ScraperConfig — _build_detail_session читает только
|
|
||||||
`.scraper_proxy_url` (structural typing, `Protocol` не требует явной реализации)."""
|
|
||||||
return SimpleNamespace(scraper_proxy_url=proxy_url)
|
|
||||||
|
|
||||||
|
|
||||||
def test_build_warmed_session_signature_now_accepts_config() -> None:
|
|
||||||
"""До фикса #2330 `build_warmed_session()` не принимала параметров вовсе —
|
|
||||||
`build_warmed_session(config=cfg)` падал бы `TypeError: unexpected keyword
|
|
||||||
argument 'config'`. Проверяем, что параметр реально появился в сигнатуре."""
|
|
||||||
params = inspect.signature(build_warmed_session).parameters
|
|
||||||
assert "config" in params
|
|
||||||
assert params["config"].default is None
|
|
||||||
|
|
||||||
|
|
||||||
async def test_build_warmed_session_with_config_propagates_real_proxy_to_session() -> None:
|
|
||||||
"""С `config.scraper_proxy_url` заданным — построенная curl_cffi-сессия реально
|
|
||||||
несёт этот proxy_url (не мок: инспектируем настоящий AsyncSession.proxies)."""
|
|
||||||
proxy_url = "http://user:pass@mproxy.local:14619"
|
|
||||||
config = _fake_config(proxy_url)
|
|
||||||
|
|
||||||
with patch.object(kit_detail, "warm_up_session", AsyncMock(return_value=True)):
|
|
||||||
session = await build_warmed_session(config=config) # type: ignore[arg-type]
|
|
||||||
try:
|
|
||||||
assert session.proxies == {"http": proxy_url, "https": proxy_url}
|
|
||||||
finally:
|
|
||||||
await session.close()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_build_warmed_session_without_config_has_no_proxy() -> None:
|
|
||||||
"""Без `config` (дефолт, единственное поведение ДО фикса) сессия строится без
|
|
||||||
прокси — ровно баг #2330: caller, не передающий config, молча теряет sticky-proxy."""
|
|
||||||
with patch.object(kit_detail, "warm_up_session", AsyncMock(return_value=True)):
|
|
||||||
session = await build_warmed_session()
|
|
||||||
try:
|
|
||||||
assert not session.proxies
|
|
||||||
finally:
|
|
||||||
await session.close()
|
|
||||||
|
|
@ -10,14 +10,13 @@ Migrates legacy scraper imports of these files to their `scraper_kit` equivalent
|
||||||
- app/tasks/avito_detail_backfill.py → AvitoScraper/BrowserFetcher/avito
|
- app/tasks/avito_detail_backfill.py → AvitoScraper/BrowserFetcher/avito
|
||||||
exceptions/fetch_detail/research_in_session/save_detail_enrichment now from
|
exceptions/fetch_detail/research_in_session/save_detail_enrichment now from
|
||||||
`scraper_kit.*`. `build_warmed_session`/`_AVITO_WARM_SEARCH_URL` stay legacy —
|
`scraper_kit.*`. `build_warmed_session`/`_AVITO_WARM_SEARCH_URL` stay legacy —
|
||||||
at the time this file was written, kit's `build_warmed_session()` built its
|
kit's `build_warmed_session()` builds its curl_cffi session via
|
||||||
curl_cffi session via `_build_detail_session()` with NO config parameter at all
|
`_build_detail_session()` with NO config parameter at all (unlike
|
||||||
(unlike `fetch_detail`, which does), so there was no caller-side way to inject
|
`fetch_detail`, which does), so there is no caller-side way to inject
|
||||||
`settings.scraper_proxy_url`. Fixed in issue #2330 (Group F2) — kit's
|
`settings.scraper_proxy_url` — migrating it would silently drop the sticky
|
||||||
`build_warmed_session()` now accepts `config`, but THIS caller still imports it
|
MGTS-proxy egress on the warm-batch path (avito_detail_backfill_use_curl=True,
|
||||||
from legacy, so migrating this specific call site remains a separate follow-up
|
the PROD DEFAULT). Same bug category as issue #2322 (scraper_kit provider
|
||||||
before the sticky MGTS-proxy egress on the warm-batch path
|
missing a config hook), different function — flagged as a new follow-up.
|
||||||
(avito_detail_backfill_use_curl=True, the prod default) can move to kit.
|
|
||||||
- app/tasks/cian_history_backfill.py → fetch_detail/save_detail_enrichment
|
- app/tasks/cian_history_backfill.py → fetch_detail/save_detail_enrichment
|
||||||
(listings block) + BrowserFetcher now from `scraper_kit.*`. The houses block
|
(listings block) + BrowserFetcher now from `scraper_kit.*`. The houses block
|
||||||
(cian_newbuilding.fetch_newbuilding/save_newbuilding_enrichment) and the
|
(cian_newbuilding.fetch_newbuilding/save_newbuilding_enrichment) and the
|
||||||
|
|
|
||||||
|
|
@ -324,20 +324,11 @@ async def research_in_session(session: AsyncSession) -> bool:
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
|
|
||||||
async def build_warmed_session(config: ScraperConfig | None = None) -> AsyncSession:
|
async def build_warmed_session() -> AsyncSession:
|
||||||
"""Построить detail-сессию (_build_detail_session) и прогреть её. Возвращает
|
"""Построить detail-сессию (_build_detail_session) и прогреть её. Возвращает
|
||||||
прогретую сессию (даже если warm-up soft-failed — caller может всё равно пробовать).
|
прогретую сессию (даже если warm-up soft-failed — caller может всё равно пробовать).
|
||||||
|
|
||||||
Strangler-инжекция (#2330): config пробрасывается в _build_detail_session тем же
|
|
||||||
способом, каким это делает fetch_detail — до этого фикса build_warmed_session
|
|
||||||
вообще не принимал config (был бы сломан любой caller, попытавшийся его передать).
|
|
||||||
Сегодняшний единственный caller (`avito_detail_backfill.py`) всё ещё импортирует
|
|
||||||
build_warmed_session из legacy-модуля, так что прод-путь этим фиксом не затронут —
|
|
||||||
это advance-prep для будущей миграции call-site на kit (avito_detail_backfill_use_curl=True
|
|
||||||
станет прод-дефолтом ТОГДА, когда caller переключится). См. issue #2330 / vault
|
|
||||||
Scraper_Kit_Legacy_Dependency_Audit_0703.
|
|
||||||
"""
|
"""
|
||||||
session = _build_detail_session(config)
|
session = _build_detail_session()
|
||||||
await warm_up_session(session)
|
await warm_up_session(session)
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue