Coverage for src / mysingle / cli / protos / utils.py: 0%
62 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-02 00:58 +0900
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-02 00:58 +0900
1"""
2공통 유틸리티 함수 모듈.
4색상 출력, 로깅, 테이블 포맷팅 등의 공통 기능을 제공합니다.
5"""
7from __future__ import annotations
9import os
10import sys
11from enum import Enum
14class Color(str, Enum):
15 """ANSI 색상 코드"""
17 RESET = "\033[0m"
18 BOLD = "\033[1m"
19 DIM = "\033[2m"
21 # 기본 색상
22 RED = "\033[31m"
23 GREEN = "\033[32m"
24 YELLOW = "\033[33m"
25 BLUE = "\033[34m"
26 MAGENTA = "\033[35m"
27 CYAN = "\033[36m"
28 WHITE = "\033[37m"
30 # 밝은 색상
31 BRIGHT_RED = "\033[91m"
32 BRIGHT_GREEN = "\033[92m"
33 BRIGHT_YELLOW = "\033[93m"
34 BRIGHT_BLUE = "\033[94m"
35 BRIGHT_MAGENTA = "\033[95m"
36 BRIGHT_CYAN = "\033[96m"
39class LogLevel(str, Enum):
40 """로그 레벨"""
42 DEBUG = "DEBUG"
43 INFO = "INFO"
44 SUCCESS = "SUCCESS"
45 WARNING = "WARNING"
46 ERROR = "ERROR"
47 STEP = "STEP"
50def colorize(text: str, color: Color, bold: bool = False) -> str:
51 """텍스트에 색상 적용"""
52 # 터미널이 색상을 지원하지 않거나 파이프로 리다이렉트된 경우 색상 코드 생략
53 if not sys.stdout.isatty() or os.environ.get("NO_COLOR"):
54 return text
55 prefix = f"{Color.BOLD.value}{color.value}" if bold else color.value
56 return f"{prefix}{text}{Color.RESET.value}"
59def log(msg: str, level: LogLevel = LogLevel.INFO) -> None:
60 """로그 출력 (레벨별 색상 및 아이콘 적용)"""
61 icons = {
62 LogLevel.DEBUG: "🔍",
63 LogLevel.INFO: "ℹ️ ",
64 LogLevel.SUCCESS: "✅",
65 LogLevel.WARNING: "⚠️ ",
66 LogLevel.ERROR: "❌",
67 LogLevel.STEP: "📋",
68 }
69 colors = {
70 LogLevel.DEBUG: Color.DIM,
71 LogLevel.INFO: Color.CYAN,
72 LogLevel.SUCCESS: Color.GREEN,
73 LogLevel.WARNING: Color.YELLOW,
74 LogLevel.ERROR: Color.RED,
75 LogLevel.STEP: Color.BRIGHT_BLUE,
76 }
77 icon = icons.get(level, " ")
78 color = colors.get(level, Color.RESET)
80 if level == LogLevel.STEP:
81 print(colorize(f"\n{icon} {msg}", color, bold=True), flush=True)
82 else:
83 print(f"{icon} {colorize(msg, color)}", flush=True)
86def log_header(title: str) -> None:
87 """섹션 헤더 출력"""
88 border = "=" * 60
89 print()
90 print(colorize(border, Color.BRIGHT_CYAN, bold=True))
91 print(colorize(f" {title}", Color.BRIGHT_CYAN, bold=True))
92 print(colorize(border, Color.BRIGHT_CYAN, bold=True))
93 print()
96def log_table(headers: list[str], rows: list[list[str]]) -> None:
97 """테이블 형식으로 출력"""
98 if not rows:
99 return
101 # 각 컬럼의 최대 너비 계산
102 col_widths = [len(h) for h in headers]
103 for row in rows:
104 for i, cell in enumerate(row):
105 col_widths[i] = max(col_widths[i], len(str(cell)))
107 # 헤더 출력
108 header_line = " ".join(h.ljust(col_widths[i]) for i, h in enumerate(headers))
109 print(colorize(header_line, Color.BRIGHT_CYAN, bold=True))
110 print(colorize("-" * len(header_line), Color.CYAN))
112 # 데이터 행 출력
113 for row in rows:
114 row_line = " ".join(
115 str(cell).ljust(col_widths[i]) for i, cell in enumerate(row)
116 )
117 print(row_line)
118 print()