Coverage for src/henrri_connect/utils.py: 97%

38 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-09 19:37 +0200

1""" 

2Module utilitaire pour les clients Henrri Connect. 

3 

4Fournit l'ensemble des fonctions utilitaires nécessaires au package ``henrri_connect``. 

5 

6Functions: 

7---------- 

8- raise_for_status: Levé une exception appropriée selon le code HTTP de la réponse. 

9- clean: Nettoie un dictionnaire en supprimant les clés dont la valeur est ``None``. 

10""" 

11from __future__ import annotations 

12 

13import logging 

14import json 

15from typing import Any 

16import httpx 

17from .exc import ( 

18 HenrriAuthError, 

19 HenrriForbiddenError, 

20 HenrriHTTPError, 

21 HenrriNotFoundError, 

22 HenrriServerError, 

23 HenrriValidationError, 

24) 

25 

26logger = logging.getLogger(__name__) 

27 

28def raise_for_status(resp: httpx.Response) -> None: 

29 """Lève une exception appropriée selon le code HTTP de la réponse. 

30 

31 Correspondance des codes HTTP : 

32 

33 - 400 : ``HenrriValidationError`` 

34 - 401 : ``HenrriAuthError`` 

35 - 403 : ``HenrriForbiddenError`` 

36 - 404 : ``HenrriNotFoundError`` 

37 - 5xx : ``HenrriServerError`` 

38 - Autres : ``HenrriHTTPError`` 

39 

40 Tente d'extraire un message d'erreur détaillé depuis la réponse JSON, 

41 ou utilise le texte brut de la réponse si le JSON est invalide. 

42 

43 Args: 

44 resp: La réponse HTTP httpx à analyser. 

45 

46 Raises: 

47 HenrriValidationError: Si le code HTTP est 400. 

48 HenrriAuthError: Si le code HTTP est 401. 

49 HenrriForbiddenError: Si le code HTTP est 403. 

50 HenrriNotFoundError: Si le code HTTP est 404. 

51 HenrriServerError: Si le code HTTP est >= 500. 

52 HenrriHTTPError: Pour tout autre code d'erreur. 

53 """ 

54 if resp.is_success: 

55 return 

56 body = None 

57 try: 

58 body = resp.json() 

59 if isinstance(body, dict): 

60 msg: str = ( 

61 body.get("detail") 

62 or body.get("title") 

63 or body.get("message") 

64 or resp.text 

65 ) 

66 else: 

67 msg = resp.text or str(body) or f"Erreur HTTP {resp.status_code}" 

68 except json.JSONDecodeError: 

69 msg = resp.text or f"Erreur HTTP {resp.status_code}" 

70 

71 sc = resp.status_code 

72 if sc == 400: 

73 logger.error("Validation error: %s", msg) 

74 raise HenrriValidationError(sc, str(msg), body) 

75 if sc == 401: 

76 logger.error("Authentication error: %s", msg) 

77 raise HenrriAuthError(sc, str(msg), body) 

78 if sc == 403: 

79 logger.error("Forbidden error: %s", msg) 

80 raise HenrriForbiddenError(sc, str(msg), body) 

81 if sc == 404: 

82 logger.error("Not found error: %s", msg) 

83 raise HenrriNotFoundError(sc, str(msg), body) 

84 if sc >= 500: 

85 logger.error("Server error: %s", msg) 

86 raise HenrriServerError(sc, str(msg), body) 

87 logger.error("HTTP error: %s", msg) 

88 raise HenrriHTTPError(sc, str(msg), body) 

89 

90def clean(params: dict[str, Any]) -> dict[str, Any]: 

91 """ 

92 Supprime les valeurs None d'un dictionnaire 

93 

94 Args: 

95 params (dict[str, Any]) : Dictionnaire contenant des valeurs None 

96 

97 Returns: 

98 dict[str, Any]: Dictionnaire sans valeurs None 

99 """ 

100 return {k: v for k, v in params.items() if v is not None}