Coverage for src / ezpl / utils / __init__.py: 72.09%

37 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-13 19:35 +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) 

44 try: 

45 return str(obj) 

46 except ( # noqa: S110 

47 Exception 

48 ): # noqa: S110 - Intentional fallback, exception handling is the purpose 

49 pass 

50 

51 # Fallback to repr() if str() fails 

52 try: 

53 return repr(obj) 

54 except ( # noqa: S110 

55 Exception 

56 ): # noqa: S110 - Intentional fallback, exception handling is the purpose 

57 pass 

58 

59 # Last resort: type name 

60 try: 

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

62 except Exception: 

63 # Ultimate fallback - should never happen 

64 return "<unknown object>" 

65 

66 

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

68 """ 

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

70 

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

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

73 

74 Args: 

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

76 

77 Returns: 

78 Message with control characters removed 

79 """ 

80 if not isinstance(message, str): 

81 message = safe_str_convert(message) 

82 

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

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

85 

86 

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

88 """ 

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

90 

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

92 and ensures valid UTF-8 encoding. 

93 

94 Args: 

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

96 

97 Returns: 

98 Sanitized message safe for file output 

99 """ 

100 message = _sanitize_base(message) 

101 

102 # Remove ANSI escape sequences 

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

104 

105 # Remove HTML/loguru tags more aggressively 

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

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

108 

109 # Replace problematic characters that might break file encoding 

110 try: 

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

112 except UnicodeEncodeError: 

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

114 

115 return message 

116 

117 

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

119 """ 

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

121 

122 Args: 

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

124 

125 Returns: 

126 Sanitized message safe for console output 

127 """ 

128 return _sanitize_base(message) 

129 

130 

131# /////////////////////////////////////////////////////////////// 

132# PUBLIC API 

133# /////////////////////////////////////////////////////////////// 

134 

135__all__ = [ 

136 "safe_str_convert", 

137 "sanitize_for_file", 

138 "sanitize_for_console", 

139]