Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pip/_vendor/rich/logging.py: 26%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

80 statements  

1import logging 

2from datetime import datetime 

3from logging import Handler, LogRecord 

4from pathlib import Path 

5from types import ModuleType 

6from typing import ClassVar, Iterable, List, Optional, Type, Union 

7 

8from pip._vendor.rich._null_file import NullFile 

9 

10from . import get_console 

11from ._log_render import FormatTimeCallable, LogRender 

12from .console import Console, ConsoleRenderable 

13from .highlighter import Highlighter, ReprHighlighter 

14from .text import Text 

15from .traceback import Traceback 

16 

17 

18class RichHandler(Handler): 

19 """A logging handler that renders output with Rich. The time / level / message and file are displayed in columns. 

20 The level is color coded, and the message is syntax highlighted. 

21 

22 Note: 

23 Be careful when enabling console markup in log messages if you have configured logging for libraries not 

24 under your control. If a dependency writes messages containing square brackets, it may not produce the intended output. 

25 

26 Args: 

27 level (Union[int, str], optional): Log level. Defaults to logging.NOTSET. 

28 console (:class:`~rich.console.Console`, optional): Optional console instance to write logs. 

29 Default will use a global console instance writing to stdout. 

30 show_time (bool, optional): Show a column for the time. Defaults to True. 

31 omit_repeated_times (bool, optional): Omit repetition of the same time. Defaults to True. 

32 show_level (bool, optional): Show a column for the level. Defaults to True. 

33 show_path (bool, optional): Show the path to the original log call. Defaults to True. 

34 enable_link_path (bool, optional): Enable terminal link of path column to file. Defaults to True. 

35 highlighter (Highlighter, optional): Highlighter to style log messages, or None to use ReprHighlighter. Defaults to None. 

36 markup (bool, optional): Enable console markup in log messages. Defaults to False. 

37 rich_tracebacks (bool, optional): Enable rich tracebacks with syntax highlighting and formatting. Defaults to False. 

38 tracebacks_width (Optional[int], optional): Number of characters used to render tracebacks, or None for full width. Defaults to None. 

39 tracebacks_code_width (int, optional): Number of code characters used to render tracebacks, or None for full width. Defaults to 88. 

40 tracebacks_extra_lines (int, optional): Additional lines of code to render tracebacks, or None for full width. Defaults to None. 

41 tracebacks_theme (str, optional): Override pygments theme used in traceback. 

42 tracebacks_word_wrap (bool, optional): Enable word wrapping of long tracebacks lines. Defaults to True. 

43 tracebacks_show_locals (bool, optional): Enable display of locals in tracebacks. Defaults to False. 

44 tracebacks_suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback. 

45 tracebacks_max_frames (int, optional): Optional maximum number of frames returned by traceback. 

46 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation. 

47 Defaults to 10. 

48 locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80. 

49 log_time_format (Union[str, TimeFormatterCallable], optional): If ``log_time`` is enabled, either string for strftime or callable that formats the time. Defaults to "[%x %X] ". 

50 keywords (List[str], optional): List of words to highlight instead of ``RichHandler.KEYWORDS``. 

51 """ 

52 

53 KEYWORDS: ClassVar[Optional[List[str]]] = [ 

54 "GET", 

55 "POST", 

56 "HEAD", 

57 "PUT", 

58 "DELETE", 

59 "OPTIONS", 

60 "TRACE", 

61 "PATCH", 

62 ] 

63 HIGHLIGHTER_CLASS: ClassVar[Type[Highlighter]] = ReprHighlighter 

64 

65 def __init__( 

66 self, 

67 level: Union[int, str] = logging.NOTSET, 

68 console: Optional[Console] = None, 

69 *, 

70 show_time: bool = True, 

71 omit_repeated_times: bool = True, 

72 show_level: bool = True, 

73 show_path: bool = True, 

74 enable_link_path: bool = True, 

75 highlighter: Optional[Highlighter] = None, 

76 markup: bool = False, 

77 rich_tracebacks: bool = False, 

78 tracebacks_width: Optional[int] = None, 

79 tracebacks_code_width: Optional[int] = 88, 

80 tracebacks_extra_lines: int = 3, 

81 tracebacks_theme: Optional[str] = None, 

82 tracebacks_word_wrap: bool = True, 

83 tracebacks_show_locals: bool = False, 

84 tracebacks_suppress: Iterable[Union[str, ModuleType]] = (), 

85 tracebacks_max_frames: int = 100, 

86 locals_max_length: int = 10, 

87 locals_max_string: int = 80, 

88 log_time_format: Union[str, FormatTimeCallable] = "[%x %X]", 

89 keywords: Optional[List[str]] = None, 

90 ) -> None: 

91 super().__init__(level=level) 

92 self.console = console or get_console() 

93 self.highlighter = highlighter or self.HIGHLIGHTER_CLASS() 

94 self._log_render = LogRender( 

95 show_time=show_time, 

96 show_level=show_level, 

97 show_path=show_path, 

98 time_format=log_time_format, 

99 omit_repeated_times=omit_repeated_times, 

100 level_width=None, 

101 ) 

102 self.enable_link_path = enable_link_path 

103 self.markup = markup 

104 self.rich_tracebacks = rich_tracebacks 

105 self.tracebacks_width = tracebacks_width 

106 self.tracebacks_extra_lines = tracebacks_extra_lines 

107 self.tracebacks_theme = tracebacks_theme 

108 self.tracebacks_word_wrap = tracebacks_word_wrap 

109 self.tracebacks_show_locals = tracebacks_show_locals 

110 self.tracebacks_suppress = tracebacks_suppress 

111 self.tracebacks_max_frames = tracebacks_max_frames 

112 self.tracebacks_code_width = tracebacks_code_width 

113 self.locals_max_length = locals_max_length 

114 self.locals_max_string = locals_max_string 

115 self.keywords = keywords 

116 

117 def get_level_text(self, record: LogRecord) -> Text: 

118 """Get the level name from the record. 

119 

120 Args: 

121 record (LogRecord): LogRecord instance. 

122 

123 Returns: 

124 Text: A tuple of the style and level name. 

125 """ 

126 level_name = record.levelname 

127 level_text = Text.styled( 

128 level_name.ljust(8), f"logging.level.{level_name.lower()}" 

129 ) 

130 return level_text 

131 

132 def emit(self, record: LogRecord) -> None: 

133 """Invoked by logging.""" 

134 message = self.format(record) 

135 traceback = None 

136 if ( 

137 self.rich_tracebacks 

138 and record.exc_info 

139 and record.exc_info != (None, None, None) 

140 ): 

141 exc_type, exc_value, exc_traceback = record.exc_info 

142 assert exc_type is not None 

143 assert exc_value is not None 

144 traceback = Traceback.from_exception( 

145 exc_type, 

146 exc_value, 

147 exc_traceback, 

148 width=self.tracebacks_width, 

149 code_width=self.tracebacks_code_width, 

150 extra_lines=self.tracebacks_extra_lines, 

151 theme=self.tracebacks_theme, 

152 word_wrap=self.tracebacks_word_wrap, 

153 show_locals=self.tracebacks_show_locals, 

154 locals_max_length=self.locals_max_length, 

155 locals_max_string=self.locals_max_string, 

156 suppress=self.tracebacks_suppress, 

157 max_frames=self.tracebacks_max_frames, 

158 ) 

159 message = record.getMessage() 

160 if self.formatter: 

161 record.message = record.getMessage() 

162 formatter = self.formatter 

163 if hasattr(formatter, "usesTime") and formatter.usesTime(): 

164 record.asctime = formatter.formatTime(record, formatter.datefmt) 

165 message = formatter.formatMessage(record) 

166 

167 message_renderable = self.render_message(record, message) 

168 log_renderable = self.render( 

169 record=record, traceback=traceback, message_renderable=message_renderable 

170 ) 

171 if isinstance(self.console.file, NullFile): 

172 # Handles pythonw, where stdout/stderr are null, and we return NullFile 

173 # instance from Console.file. In this case, we still want to make a log record 

174 # even though we won't be writing anything to a file. 

175 self.handleError(record) 

176 else: 

177 try: 

178 self.console.print(log_renderable) 

179 except Exception: 

180 self.handleError(record) 

181 

182 def render_message(self, record: LogRecord, message: str) -> "ConsoleRenderable": 

183 """Render message text in to Text. 

184 

185 Args: 

186 record (LogRecord): logging Record. 

187 message (str): String containing log message. 

188 

189 Returns: 

190 ConsoleRenderable: Renderable to display log message. 

191 """ 

192 use_markup = getattr(record, "markup", self.markup) 

193 message_text = Text.from_markup(message) if use_markup else Text(message) 

194 

195 highlighter = getattr(record, "highlighter", self.highlighter) 

196 if highlighter: 

197 message_text = highlighter(message_text) 

198 

199 if self.keywords is None: 

200 self.keywords = self.KEYWORDS 

201 

202 if self.keywords: 

203 message_text.highlight_words(self.keywords, "logging.keyword") 

204 

205 return message_text 

206 

207 def render( 

208 self, 

209 *, 

210 record: LogRecord, 

211 traceback: Optional[Traceback], 

212 message_renderable: "ConsoleRenderable", 

213 ) -> "ConsoleRenderable": 

214 """Render log for display. 

215 

216 Args: 

217 record (LogRecord): logging Record. 

218 traceback (Optional[Traceback]): Traceback instance or None for no Traceback. 

219 message_renderable (ConsoleRenderable): Renderable (typically Text) containing log message contents. 

220 

221 Returns: 

222 ConsoleRenderable: Renderable to display log. 

223 """ 

224 path = Path(record.pathname).name 

225 level = self.get_level_text(record) 

226 time_format = None if self.formatter is None else self.formatter.datefmt 

227 log_time = datetime.fromtimestamp(record.created) 

228 

229 log_renderable = self._log_render( 

230 self.console, 

231 [message_renderable] if not traceback else [message_renderable, traceback], 

232 log_time=log_time, 

233 time_format=time_format, 

234 level=level, 

235 path=path, 

236 line_no=record.lineno, 

237 link_path=record.pathname if self.enable_link_path else None, 

238 ) 

239 return log_renderable 

240 

241 

242if __name__ == "__main__": # pragma: no cover 

243 from time import sleep 

244 

245 FORMAT = "%(message)s" 

246 # FORMAT = "%(asctime)-15s - %(levelname)s - %(message)s" 

247 logging.basicConfig( 

248 level="NOTSET", 

249 format=FORMAT, 

250 datefmt="[%X]", 

251 handlers=[RichHandler(rich_tracebacks=True, tracebacks_show_locals=True)], 

252 ) 

253 log = logging.getLogger("rich") 

254 

255 log.info("Server starting...") 

256 log.info("Listening on http://127.0.0.1:8080") 

257 sleep(1) 

258 

259 log.info("GET /index.html 200 1298") 

260 log.info("GET /imgs/backgrounds/back1.jpg 200 54386") 

261 log.info("GET /css/styles.css 200 54386") 

262 log.warning("GET /favicon.ico 404 242") 

263 sleep(1) 

264 

265 log.debug( 

266 "JSONRPC request\n--> %r\n<-- %r", 

267 { 

268 "version": "1.1", 

269 "method": "confirmFruitPurchase", 

270 "params": [["apple", "orange", "mangoes", "pomelo"], 1.123], 

271 "id": "194521489", 

272 }, 

273 {"version": "1.1", "result": True, "error": None, "id": "194521489"}, 

274 ) 

275 log.debug( 

276 "Loading configuration file /adasd/asdasd/qeqwe/qwrqwrqwr/sdgsdgsdg/werwerwer/dfgerert/ertertert/ertetert/werwerwer" 

277 ) 

278 log.error("Unable to find 'pomelo' in database!") 

279 log.info("POST /jsonrpc/ 200 65532") 

280 log.info("POST /admin/ 401 42234") 

281 log.warning("password was rejected for admin site.") 

282 

283 def divide() -> None: 

284 number = 1 

285 divisor = 0 

286 foos = ["foo"] * 100 

287 log.debug("in divide") 

288 try: 

289 number / divisor 

290 except: 

291 log.exception("An error of some kind occurred!") 

292 

293 divide() 

294 sleep(1) 

295 log.critical("Out of memory!") 

296 log.info("Server exited with code=-1") 

297 log.info("[bold]EXITING...[/bold]", extra=dict(markup=True))