Coverage for custom_components/supernotify/common.py: 98%

54 statements  

« prev     ^ index     » next       coverage.py v7.6.8, created at 2024-12-28 14:21 +0000

1"""Miscellaneous helper functions. 

2 

3No dependencies permitted 

4""" 

5 

6import time 

7from dataclasses import dataclass, field 

8from typing import Any 

9 

10 

11def format_timestamp(v: float | None) -> str | None: 

12 return time.strftime("%H:%M:%S", time.localtime(v)) if v else None 

13 

14 

15def safe_get(probably_a_dict: dict | None, key: Any, default: Any = None) -> Any: 

16 probably_a_dict = probably_a_dict or {} 

17 return probably_a_dict.get(key, default) 

18 

19 

20def safe_extend(target: list, extension: list | tuple | Any) -> list: 

21 if isinstance(extension, list | tuple): 

22 target.extend(extension) 

23 elif extension: 

24 target.append(extension) 

25 return target 

26 

27 

28def ensure_list(v: Any) -> list: 

29 if v is None: 

30 return [] 

31 if isinstance(v, list): 

32 return v 

33 if isinstance(v, tuple): 

34 return list(v) 

35 return [v] 

36 

37 

38def ensure_dict(v: Any, default: Any = None) -> dict: 

39 if v is None: 

40 return {} 

41 if isinstance(v, dict): 

42 return v 

43 if isinstance(v, set | list): 

44 return dict.fromkeys(v, default) 

45 return {v: default} 

46 

47 

48def update_dict_list(target: list[dict[Any, Any]], to_add: list[dict], to_remove: list[dict]) -> list[dict]: 

49 updated = [d for d in target if d not in to_remove] 

50 updated.extend(to_add) 

51 return updated 

52 

53 

54@dataclass 

55class CallRecord: 

56 elapsed: float = field() 

57 domain: str | None = field(default=None) 

58 service: str | None = field(default=None) 

59 action_data: dict | None = field(default=None) 

60 exception: str | None = field(default=None) 

61 

62 def contents(self) -> tuple: 

63 if self.exception: 

64 return (self.domain, self.service, self.action_data, self.exception, self.elapsed) 

65 return (self.domain, self.service, self.action_data, self.elapsed) 

66 

67 

68@dataclass 

69class DebugTrace: 

70 message: str | None = field(default=None) 

71 title: str | None = field(default=None) 

72 data: dict | None = field(default_factory=lambda: {}) 

73 target: list | str | None = field(default=None) 

74 resolved: dict[str, dict] = field(init=False, default_factory=lambda: {}) 

75 

76 def contents(self) -> tuple: 

77 return (self.message, self.title, self.data, self.target, self.resolved)