Module connpy.utils

Functions

def log_cleaner(data: str) ‑> str
Expand source code
def log_cleaner(data: str) -> str:
    """
    Stateless utility to remove ANSI sequences and process cursor movements.
    """
    if not data:
        return ""
            
    lines = data.split('\n')
    cleaned_lines = []
    
    # Regex to capture: ANSI sequences, control characters (\r, \b, etc), and plain text chunks
    token_re = re.compile(r'(\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/ ]*[@-~])|\r|\b|\x7f|[\x00-\x1F]|[^\x1B\r\b\x7f\x00-\x1F]+)')
    
    for line in lines:
        buffer = []
        cursor = 0
        
        for token in token_re.findall(line):
            if token == '\r':
                cursor = 0
            elif token in ('\b', '\x7f'):
                if cursor > 0:
                    cursor -= 1
            elif token.startswith('\x1B[') and len(token) >= 3:
                # Parse CSI: \x1B[ <params> <final_char>
                final = token[-1]
                param_str = token[2:-1]
                n = int(param_str) if param_str.isdigit() else 1

                if final == 'D':   # CUB – Cursor Back
                    cursor = max(0, cursor - n)
                elif final == 'C': # CUF – Cursor Forward
                    cursor = min(len(buffer), cursor + n)
                elif final == 'K': # EL  – Erase in Line
                    if n == 0 or param_str == '':      # Clear to end
                        buffer = buffer[:cursor]
                    elif n == 1:                         # Clear to start
                        buffer[:cursor] = [' '] * cursor
                    elif n == 2:                         # Clear entire line
                        buffer = []
                        cursor = 0
                elif final == 'G': # CHA – Cursor Horizontal Absolute (1-indexed)
                    cursor = max(0, n - 1)
                    # Pad buffer if cursor is beyond current length
                    if cursor > len(buffer):
                        buffer.extend([' '] * (cursor - len(buffer)))
                elif final == 'P': # DCH – Delete Characters
                    del buffer[cursor:cursor + n]
                elif final == '@': # ICH – Insert Characters
                    buffer[cursor:cursor] = [' '] * n
                # All other CSI sequences are silently discarded
            elif token.startswith('\x1B'):
                continue
            elif len(token) == 1 and ord(token) < 32:
                continue
            else:
                for char in token:
                    if cursor == len(buffer):
                        buffer.append(char)
                    else:
                        buffer[cursor] = char
                    cursor += 1
        cleaned_lines.append("".join(buffer))
        
    return "\n".join(cleaned_lines).replace('\n\n', '\n').strip()

Stateless utility to remove ANSI sequences and process cursor movements.