Source code for human_requests.browsers.families.base

from __future__ import annotations

from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any, Dict, Literal, Optional

from playwright.async_api import Browser, BrowserContext, StorageState

[docs] Family = Literal["playwright", "patchright", "camoufox"]
[docs] PlaywrightEngine = Literal["chromium", "firefox", "webkit"]
[docs] class DesiredConfig: """Unified "desired" configuration for all families."""
[docs] __slots__ = ("family", "engine", "headless", "stealth", "launch_opts")
def __init__( self, *, family: Family, engine: PlaywrightEngine | None, stealth: bool, launch_opts: Dict[str, Any], ) -> None:
[docs] self.family = family
[docs] self.engine = engine
[docs] self.stealth = stealth
[docs] self.launch_opts = dict(launch_opts) # копия
[docs] class BrowserFamily(ABC): """Family interface. Implements idempotent start and soft restarts internally.""" @property @abstractmethod
[docs] def name(self) -> Family: # noqa: D401 """Family name.""" raise NotImplementedError
@abstractmethod
[docs] async def start(self, cfg: DesiredConfig) -> None: """Idempotent launch/restart according to cfg.""" raise NotImplementedError
@abstractmethod
[docs] async def close(self) -> None: """Close all family resources (browser + runtime).""" raise NotImplementedError
@property @abstractmethod
[docs] def browser(self) -> Optional[Browser]: """Current Browser or None if not started.""" raise NotImplementedError
[docs] async def new_context( self, *, storage_state: StorageState | str | Path | None = None, ) -> BrowserContext: await self._ensure() assert self.browser is not None return await self.browser.new_context(storage_state=storage_state)
async def _ensure(self) -> None: if self.browser is None: # Семейство само знает «последнюю cfg». Упрощённо: бросаем, если не стартовало. raise RuntimeError(f"{self.name}: not started yet")