generated from ITProKyle/generic-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rename file, add default styles, add console handler
- Loading branch information
Showing
6 changed files
with
211 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
"""Custom console :class:`~rich.logging.RichHandler`.""" | ||
|
||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING | ||
|
||
from rich.logging import RichHandler | ||
from rich.markup import escape | ||
from rich.text import Text | ||
|
||
if TYPE_CHECKING: | ||
import logging | ||
|
||
from rich.console import ConsoleRenderable | ||
|
||
|
||
class ConsoleHandler(RichHandler): | ||
"""Custom console :class:`~rich.logging.RichHandler`.""" | ||
|
||
def _determine_should_escape(self, record: logging.LogRecord) -> bool: | ||
"""Determine if a log message should be passed to :function:`~rich.markup.escape`. | ||
This can be overridden in subclasses for more control. | ||
""" | ||
return self._determine_use_markup(record) and getattr(record, "escape", False) | ||
|
||
def _determine_use_markup(self, record: logging.LogRecord) -> bool: | ||
"""Determine if markup should be used for a log record.""" | ||
return getattr(record, "markup", self.markup) | ||
|
||
def render_message(self, record: logging.LogRecord, message: str) -> ConsoleRenderable: | ||
"""Render message text in to Text. | ||
Args: | ||
record: logging Record. | ||
message: String containing log message. | ||
Returns: | ||
ConsoleRenderable: Renderable to display log message. | ||
""" | ||
if self._determine_should_escape(record): | ||
message = escape(message) | ||
return super().render_message(*self._style_message(record, message)) | ||
|
||
def _style_message( | ||
self, | ||
record: logging.LogRecord, | ||
message: str, | ||
) -> tuple[logging.LogRecord, str]: | ||
"""Apply style to the message.""" | ||
if not self._determine_use_markup(record): | ||
return record, message | ||
return record, Text.from_markup(message, style=record.levelname.lower()).markup | ||
|
||
def get_level_text(self, record: logging.LogRecord) -> Text: | ||
"""Get the level name from the record. | ||
Args: | ||
record: LogRecord instance. | ||
Returns: | ||
Text: A tuple of the style and level name. | ||
""" | ||
level_name = record.levelname | ||
return Text.styled(f"[{level_name}]".ljust(9), f"logging.level.{level_name.lower()}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
"""Test f_lib.logging._console_handler.""" | ||
|
||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING | ||
from unittest.mock import Mock | ||
|
||
import pytest | ||
|
||
from f_lib.logging._console_handler import ConsoleHandler | ||
|
||
if TYPE_CHECKING: | ||
from pytest_mock import MockerFixture | ||
|
||
MODULE = "f_lib.logging._console_handler" | ||
|
||
|
||
class TestConsoleHandler: | ||
"""Test ConsoleHandler.""" | ||
|
||
@pytest.mark.parametrize( | ||
("escape", "markup", "expected"), | ||
[(False, False, False), (False, True, False), (True, False, False), (True, True, True)], | ||
) | ||
def test__determine_should_escape( | ||
self, escape: bool, expected: bool, markup: bool, mocker: MockerFixture | ||
) -> None: | ||
"""Test _determine_should_escape.""" | ||
determine_use_markup = mocker.patch.object( | ||
ConsoleHandler, "_determine_use_markup", return_value=markup | ||
) | ||
record = Mock(escape=escape) | ||
assert ConsoleHandler()._determine_should_escape(record) is expected | ||
determine_use_markup.assert_called_once_with(record) | ||
|
||
@pytest.mark.parametrize( | ||
("handler_markup", "record_markup", "expected"), | ||
[ | ||
(False, False, False), | ||
(False, True, True), | ||
(True, False, False), | ||
(True, True, True), | ||
(False, None, False), | ||
(True, None, True), | ||
], | ||
) | ||
def test__determine_use_markup( | ||
self, expected: bool, handler_markup: bool, record_markup: bool | None | ||
) -> None: | ||
"""Test _determine_use_markup.""" | ||
record = Mock(markup=record_markup) | ||
if record_markup is None: | ||
del record.markup | ||
assert ConsoleHandler(markup=handler_markup)._determine_use_markup(record) is expected | ||
|
||
def test__style_message(self, mocker: MockerFixture) -> None: | ||
"""Test _style_message.""" | ||
record = Mock() | ||
determine_use_markup = mocker.patch.object( | ||
ConsoleHandler, "_determine_use_markup", return_value=False | ||
) | ||
from_markup = mocker.patch(f"{MODULE}.Text.from_markup") | ||
assert ConsoleHandler()._style_message(record, "msg") == (record, "msg") | ||
determine_use_markup.assert_called_once_with(record) | ||
from_markup.assert_not_called() | ||
|
||
def test__style_message_markup(self, mocker: MockerFixture) -> None: | ||
"""Test _style_message with markup.""" | ||
record = Mock(levelname="INFO") | ||
determine_use_markup = mocker.patch.object( | ||
ConsoleHandler, "_determine_use_markup", return_value=True | ||
) | ||
from_markup = mocker.patch( | ||
f"{MODULE}.Text.from_markup", return_value=Mock(markup="success") | ||
) | ||
assert ConsoleHandler()._style_message(record, "msg") == (record, "success") | ||
determine_use_markup.assert_called_once_with(record) | ||
from_markup.assert_called_once_with("msg", style="info") | ||
|
||
def test_get_level_text(self, mocker: MockerFixture) -> None: | ||
"""Test get_level_text.""" | ||
record = Mock(levelname="INFO") | ||
styled = mocker.patch(f"{MODULE}.Text.styled", return_value="styled") | ||
assert ConsoleHandler().get_level_text(record) == styled.return_value | ||
styled.assert_called_once_with("[INFO] ", "logging.level.info") | ||
|
||
def test_render_message(self, mocker: MockerFixture) -> None: | ||
"""Test render_message.""" | ||
record = Mock(name="record") | ||
determine_should_escape = mocker.patch.object( | ||
ConsoleHandler, "_determine_should_escape", return_value=True | ||
) | ||
escape = mocker.patch(f"{MODULE}.escape", return_value="escaped") | ||
render_message = mocker.patch( | ||
f"{MODULE}.RichHandler.render_message", return_value="rendered" | ||
) | ||
style_message = mocker.patch.object( | ||
ConsoleHandler, "_style_message", return_value=("style", "message") | ||
) | ||
assert ConsoleHandler().render_message(record, "message") == render_message.return_value | ||
determine_should_escape.assert_called_once_with(record) | ||
escape.assert_called_once_with("message") | ||
style_message.assert_called_once_with(record, "escaped") | ||
render_message.assert_called_once_with("style", "message") |
4 changes: 2 additions & 2 deletions
4
tests/unit/logging/test__highlighters.py → ...t/logging/test__extendable_highlighter.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters