Coverage for human_requests/abstraction/response.py: 91%
46 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-28 00:39 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-28 00:39 +0000
1from __future__ import annotations
3from dataclasses import dataclass
4from time import time
5from typing import TYPE_CHECKING, Literal, Optional
7from .http import URL
8from .json_debug import loads_json_debug
9from .request import FetchRequest
11if TYPE_CHECKING:
12 from ..human_page import HumanPage
15@dataclass(frozen=True)
16class FetchResponse:
17 """Represents the response of a request."""
19 request: FetchRequest
20 """The request that was made."""
22 page: "HumanPage"
23 """The page that made the request."""
25 url: URL
26 """The URL of the response. Due to redirects, it can differ from `request.url`."""
28 headers: dict
29 """The headers of the response."""
31 raw: bytes
32 """The raw body of the response."""
34 status_code: int
35 """The status code of the response."""
37 status_text: str
38 """Человеко-читаемое представление status_code"""
40 redirected: bool
41 """Был ли ответ сформировапн в следствии редиректа"""
43 type: Literal["basic", "cors", "error", "opaque", "opaqueredirect"]
45 duration: float
46 """The duration of the request in seconds."""
48 end_time: float
49 """Current time in seconds since the Epoch."""
51 @property
52 def text(self) -> str:
53 """The body of the response."""
54 defchar = "utf-8"
55 ct = self.headers.get("content-type", "")
56 charset = ct.split("charset=")[-1] if "charset=" in ct else defchar
57 return self.raw.decode(charset, errors="replace")
59 def json(self) -> dict | list:
60 to_return = loads_json_debug(self.text)
61 assert isinstance(to_return, list) or isinstance(
62 to_return, dict
63 ), f"Response body is not JSON: {type(self.text).__name__}"
64 return to_return
66 def seconds_ago(self) -> float:
67 """How long ago was the request?"""
68 return time() - self.end_time
70 async def render(
71 self,
72 retry: int = 2,
73 timeout: Optional[float] = None,
74 wait_until: Literal["commit", "load", "domcontentloaded", "networkidle"] = "commit",
75 referer: Optional[str] = None,
76 ) -> "HumanPage":
77 """Renders the response content in the current browser.
78 It will look like we requested it through the browser from the beginning.
80 Recommended to use in cases when the server returns a JS challenge instead of a response."""
81 page = await self.page.context.new_page()
82 await page.goto_render(
83 self, wait_until=wait_until, referer=referer, timeout=timeout, retry=retry
84 )
85 return page