Coverage for src\baobab_web_api_caller\core\baobab_response.py: 86%
39 statements
« prev ^ index » next coverage.py v7.10.3, created at 2026-03-21 12:10 +0100
« prev ^ index » next coverage.py v7.10.3, created at 2026-03-21 12:10 +0100
1"""Modèle de réponse HTTP."""
3from __future__ import annotations
5from dataclasses import dataclass
6from types import MappingProxyType
7from typing import Mapping
9from baobab_web_api_caller.exceptions.configuration_exception import ConfigurationException
12@dataclass(frozen=True, slots=True)
13class BaobabResponse:
14 """Représentation typée d'une réponse HTTP.
16 :param status_code: Code de statut HTTP.
17 :type status_code: int
18 :param headers: En-têtes HTTP.
19 :type headers: Mapping[str, str]
20 :param text: Contenu texte (si disponible).
21 :type text: str | None
22 :param content: Contenu binaire brut (si disponible).
23 :type content: bytes | None
24 :param json_data: Contenu JSON déjà décodé (si disponible).
25 :type json_data: object | None
26 :raises ConfigurationException: Si les paramètres de la réponse sont invalides.
27 """
29 status_code: int
30 headers: Mapping[str, str]
31 text: str | None = None
32 content: bytes | bytearray | None = None
33 json_data: object | None = None
35 def __post_init__(self) -> None:
36 if not isinstance(self.status_code, int):
37 raise ConfigurationException("status_code must be an int")
38 if self.status_code < 100 or self.status_code > 599:
39 raise ConfigurationException("status_code must be between 100 and 599")
41 object.__setattr__(self, "headers", self._freeze_headers(self.headers))
43 if self.text is not None and not isinstance(self.text, str): 43 ↛ 44line 43 didn't jump to line 44 because the condition on line 43 was never true
44 raise ConfigurationException("text must be a string when provided")
45 if isinstance(self.content, bytearray):
46 object.__setattr__(self, "content", bytes(self.content))
47 if self.content is not None and not isinstance(self.content, bytes): 47 ↛ 48line 47 didn't jump to line 48 because the condition on line 47 was never true
48 raise ConfigurationException("content must be bytes when provided")
50 @staticmethod
51 def _freeze_headers(headers: Mapping[str, str]) -> Mapping[str, str]:
52 if not isinstance(headers, Mapping): 52 ↛ 53line 52 didn't jump to line 53 because the condition on line 52 was never true
53 raise ConfigurationException("headers must be a mapping")
55 frozen: dict[str, str] = {}
56 for k, v in headers.items():
57 if not isinstance(k, str) or k.strip() == "": 57 ↛ 58line 57 didn't jump to line 58 because the condition on line 57 was never true
58 raise ConfigurationException("headers keys must be non-empty strings")
59 if not isinstance(v, str):
60 raise ConfigurationException("headers values must be strings")
61 frozen[k] = v
62 return MappingProxyType(frozen)
64 @property
65 def is_success(self) -> bool:
66 """Indique si le status code correspond à une réussite HTTP (2xx)."""
68 return 200 <= self.status_code <= 299