Coverage for human_requests / abstraction / response.py: 91%

45 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-25 10:02 +0000

1import json 

2from dataclasses import dataclass 

3from time import time 

4from typing import TYPE_CHECKING, Literal, Optional 

5 

6from .http import URL 

7from .request import FetchRequest 

8 

9if TYPE_CHECKING: 

10 from ..human_page import HumanPage 

11 

12 

13@dataclass(frozen=True) 

14class FetchResponse: 

15 """Represents the response of a request.""" 

16 

17 request: FetchRequest 

18 """The request that was made.""" 

19 

20 page: "HumanPage" 

21 """The page that made the request.""" 

22 

23 url: URL 

24 """The URL of the response. Due to redirects, it can differ from `request.url`.""" 

25 

26 headers: dict 

27 """The headers of the response.""" 

28 

29 raw: bytes 

30 """The raw body of the response.""" 

31 

32 status_code: int 

33 """The status code of the response.""" 

34 

35 status_text: str 

36 """Человеко-читаемое представление status_code""" 

37 

38 redirected: bool 

39 """Был ли ответ сформировапн в следствии редиректа""" 

40 

41 type: Literal["basic", "cors", "error", "opaque", "opaqueredirect"] 

42 

43 duration: float 

44 """The duration of the request in seconds.""" 

45 

46 end_time: float 

47 """Current time in seconds since the Epoch.""" 

48 

49 @property 

50 def text(self) -> str: 

51 """The body of the response.""" 

52 defchar = "utf-8" 

53 ct = self.headers.get("content-type", "") 

54 charset = ct.split("charset=")[-1] if "charset=" in ct else defchar 

55 return self.raw.decode(charset, errors="replace") 

56 

57 def json(self) -> dict | list: 

58 to_return = json.loads(self.text) 

59 assert isinstance(to_return, list) or isinstance( 

60 to_return, dict 

61 ), f"Response body is not JSON: {type(self.text).__name__}" 

62 return to_return 

63 

64 def seconds_ago(self) -> float: 

65 """How long ago was the request?""" 

66 return time() - self.end_time 

67 

68 async def render( 

69 self, 

70 retry: int = 2, 

71 timeout: Optional[float] = None, 

72 wait_until: Literal["commit", "load", "domcontentloaded", "networkidle"] = "commit", 

73 referer: Optional[str] = None, 

74 ) -> "HumanPage": 

75 """Renders the response content in the current browser. 

76 It will look like we requested it through the browser from the beginning. 

77 

78 Recommended to use in cases when the server returns a JS challenge instead of a response.""" 

79 page = await self.page.context.new_page() 

80 await page.goto_render( 

81 self, wait_until=wait_until, referer=referer, timeout=timeout, retry=retry 

82 ) 

83 return page