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
« 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# ///////////////////////////////////////////////////////////////
6"""
7Utils module for Ezpl logging framework.
9This module provides robust message conversion and sanitization functions
10used throughout the framework.
11"""
13from __future__ import annotations
15# ///////////////////////////////////////////////////////////////
16# IMPORTS
17# ///////////////////////////////////////////////////////////////
18# Standard library imports
19import re
20from typing import Any
22# ///////////////////////////////////////////////////////////////
23# FUNCTIONS
24# ///////////////////////////////////////////////////////////////
27def safe_str_convert(obj: Any) -> str:
28 """
29 Safely convert any object to string with multiple fallback strategies.
31 Args:
32 obj: Object to convert to string
34 Returns:
35 String representation of the object (never fails)
36 """
37 if obj is None:
38 return "None"
40 if isinstance(obj, str):
41 return obj
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
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
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>"
67def _sanitize_base(message: Any) -> str:
68 """
69 Base sanitization: convert to string and remove control characters.
71 Removes null bytes and control characters (except newlines and tabs).
72 This is the shared logic for both file and console sanitization.
74 Args:
75 message: Message to sanitize (can be any type, will be converted to str)
77 Returns:
78 Message with control characters removed
79 """
80 if not isinstance(message, str):
81 message = safe_str_convert(message)
83 # Remove null bytes and other control characters (except newlines and tabs)
84 return re.sub(r"[\x00-\x08\x0B-\x0C\x0E-\x1F]", "", message)
87def sanitize_for_file(message: Any) -> str:
88 """
89 Sanitize a message for file output by removing problematic characters.
91 Applies base sanitization, then removes ANSI sequences, HTML/loguru tags,
92 and ensures valid UTF-8 encoding.
94 Args:
95 message: Message to sanitize (can be any type, will be converted to str)
97 Returns:
98 Sanitized message safe for file output
99 """
100 message = _sanitize_base(message)
102 # Remove ANSI escape sequences
103 message = re.sub(r"\x1B\[[0-9;]*[a-zA-Z]", "", message)
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
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")
115 return message
118def sanitize_for_console(message: Any) -> str:
119 """
120 Sanitize a message for console output (less aggressive, Rich handles most cases).
122 Args:
123 message: Message to sanitize (can be any type, will be converted to str)
125 Returns:
126 Sanitized message safe for console output
127 """
128 return _sanitize_base(message)
131# ///////////////////////////////////////////////////////////////
132# PUBLIC API
133# ///////////////////////////////////////////////////////////////
135__all__ = [
136 "safe_str_convert",
137 "sanitize_for_file",
138 "sanitize_for_console",
139]