Coverage for src / ezplog / utils / __init__.py: 75.61%

35 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-03 16:27 +0000

1# /////////////////////////////////////////////////////////////// 

2# EZPL - Utils Module 

3# Project: ezpl 

4# /////////////////////////////////////////////////////////////// 

5 

6""" 

7Utils module for Ezpl logging framework. 

8 

9This module provides robust message conversion and sanitization functions 

10used throughout the framework. 

11""" 

12 

13from __future__ import annotations 

14 

15# /////////////////////////////////////////////////////////////// 

16# IMPORTS 

17# /////////////////////////////////////////////////////////////// 

18# Standard library imports 

19import re 

20from typing import Any 

21 

22# /////////////////////////////////////////////////////////////// 

23# FUNCTIONS 

24# /////////////////////////////////////////////////////////////// 

25 

26 

27def safe_str_convert(obj: Any) -> str: 

28 """ 

29 Safely convert any object to string with multiple fallback strategies. 

30 

31 Args: 

32 obj: Object to convert to string 

33 

34 Returns: 

35 String representation of the object (never fails) 

36 """ 

37 if obj is None: 

38 return "None" 

39 

40 if isinstance(obj, str): 

41 return obj 

42 

43 # Try str() first (most common case), then fallback to repr(). 

44 try: 

45 return str(obj) 

46 except Exception: 

47 try: 

48 return repr(obj) 

49 except Exception: 

50 try: 

51 return f"<{type(obj).__name__} object>" 

52 except Exception: 

53 # Ultimate fallback - should never happen 

54 return "<unknown object>" 

55 

56 

57def _sanitize_base(message: Any) -> str: 

58 """ 

59 Base sanitization: convert to string and remove control characters. 

60 

61 Removes null bytes and control characters (except newlines and tabs). 

62 This is the shared logic for both file and console sanitization. 

63 

64 Args: 

65 message: Message to sanitize (can be any type, will be converted to str) 

66 

67 Returns: 

68 Message with control characters removed 

69 """ 

70 if not isinstance(message, str): 

71 message = safe_str_convert(message) 

72 

73 # Remove null bytes and other control characters (except newlines and tabs) 

74 return re.sub(r"[\x00-\x08\x0B-\x0C\x0E-\x1F]", "", message) 

75 

76 

77def sanitize_for_file(message: Any) -> str: 

78 """ 

79 Sanitize a message for file output by removing problematic characters. 

80 

81 Applies base sanitization, then removes ANSI sequences, HTML/loguru tags, 

82 and ensures valid UTF-8 encoding. 

83 

84 Args: 

85 message: Message to sanitize (can be any type, will be converted to str) 

86 

87 Returns: 

88 Sanitized message safe for file output 

89 """ 

90 message = _sanitize_base(message) 

91 

92 # Remove ANSI escape sequences 

93 message = re.sub(r"\x1B\[[0-9;]*[a-zA-Z]", "", message) 

94 

95 # Remove HTML/loguru tags more aggressively 

96 message = re.sub(r"</?>", "", message) # Remove all < and > 

97 message = re.sub(r"<[^>]+>", "", message) # Remove any remaining tags 

98 

99 # Replace problematic characters that might break file encoding 

100 try: 

101 message.encode("utf-8", errors="strict") 

102 except UnicodeEncodeError: 

103 message = message.encode("utf-8", errors="replace").decode("utf-8") 

104 

105 return message 

106 

107 

108def sanitize_for_console(message: Any) -> str: 

109 """ 

110 Sanitize a message for console output (less aggressive, Rich handles most cases). 

111 

112 Args: 

113 message: Message to sanitize (can be any type, will be converted to str) 

114 

115 Returns: 

116 Sanitized message safe for console output 

117 """ 

118 return _sanitize_base(message) 

119 

120 

121# /////////////////////////////////////////////////////////////// 

122# PUBLIC API 

123# /////////////////////////////////////////////////////////////// 

124 

125__all__ = [ 

126 "safe_str_convert", 

127 "sanitize_for_file", 

128 "sanitize_for_console", 

129]