Skip to content

Commit fc8557d

Browse files
committed
ConsoleBase now defines a default logging formatter
This is used unless logging_handlers defines a formatter. Enable not specifying a level via logging_handlers.
1 parent 07ca56d commit fc8557d

File tree

5 files changed

+66
-29
lines changed

5 files changed

+66
-29
lines changed

examples/output_console.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ def __init__(self, parent=None):
5454
# See this method for how to configure uiAllLog to show all logging
5555
# messages of all levels
5656
self.all_logging_level_warning()
57+
# Configure the default logging formatter for this widget.
58+
self.uiAllLog.logging_formatter_str = (
59+
"<<%(levelname)s| %(name)s: Line: %(lineno)d>> %(message)s"
60+
)
5761

5862
# Configure uiSelectLog to show logging messages from specific handlers
5963
self.uiSelectLog.logging_handlers = [
@@ -80,6 +84,7 @@ def all_logging_level_debug(self):
8084
"root,level=DEBUG",
8185
"PyQt5,level=CRITICAL",
8286
"PyQt6,level=CRITICAL",
87+
"logger_b,fmt=[%(levelname)s:%(name)s] %(message)s",
8388
]
8489

8590
def all_logging_level_warning(self):
@@ -92,6 +97,8 @@ def all_logging_level_warning(self):
9297
# logging messages created when first showing the PrEditor instance.
9398
"PyQt5,level=CRITICAL",
9499
"PyQt6,level=CRITICAL",
100+
# Replace the default logging_formatter_str formatter only for logger_b
101+
"logger_b,fmt=[%(levelname)s:%(name)s] %(message)s",
95102
]
96103

97104
def clear_all(self):

examples/output_console.ui

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@
7777
</property>
7878
<layout class="QVBoxLayout" name="verticalLayout_4">
7979
<item>
80-
<widget class="OutputConsole" name="uiSelectLog"/>
80+
<widget class="OutputConsole" name="uiSelectLog">
81+
<property name="logging_formatter_str" stdset="0">
82+
<string>&lt;&lt;%(levelname)s| %(name)s: Line: %(lineno)d&gt;&gt; %(message)s</string>
83+
</property>
84+
</widget>
8185
</item>
8286
</layout>
8387
</widget>

preditor/gui/console_base.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Optional
99

1010
from Qt import QtCompat
11-
from Qt.QtCore import Qt
11+
from Qt.QtCore import Property, Qt
1212
from Qt.QtGui import (
1313
QColor,
1414
QFontMetrics,
@@ -21,7 +21,7 @@
2121

2222
from .. import instance, resourcePath, stream
2323
from ..constants import StreamType
24-
from ..stream.console_handler import HandlerInfo
24+
from ..stream.console_handler import FormatterDescriptor, HandlerInfo
2525
from ..utils.cute import QtPropertyInit
2626
from .codehighlighter import CodeHighlighter
2727
from .loggerwindow import LoggerWindow
@@ -31,6 +31,12 @@
3131
class ConsoleBase(QTextEdit):
3232
"""Base class for a text widget used to show stdout/stderr writes."""
3333

34+
_default_format = (
35+
'%(levelname)s %(module)s.%(funcName)s line:%(lineno)d - %(message)s'
36+
)
37+
logging_formatter = FormatterDescriptor(default=_default_format)
38+
"""Used to format logging messages if logging_handlers doesn't define it."""
39+
3440
def __init__(self, parent: QWidget, controller: Optional[LoggerWindow] = None):
3541
super().__init__(parent)
3642
self.controller = controller
@@ -597,6 +603,8 @@ def write_log(self, log_data, stream_type=StreamType.CONSOLE):
597603
formatter = handler
598604
if logging_info.formatter:
599605
formatter = logging_info.formatter
606+
elif self.logging_formatter:
607+
formatter = self.logging_formatter
600608
msg = formatter.format(record)
601609
self.write(f'{msg}\n', stream_type=stream_type)
602610

@@ -789,6 +797,18 @@ def _write(self, msg, stream_type=StreamType.STDOUT):
789797
stdoutColor = QtPropertyInit('_stdoutColor', QColor(17, 154, 255))
790798
stringColor = QtPropertyInit('_stringColor', QColor(255, 128, 0))
791799

800+
@Property(str)
801+
def logging_formatter_str(self):
802+
"""QtProperty exposing logging_formatter as a string for QtDesigner."""
803+
try:
804+
return self.logging_formatter._fmt
805+
except AttributeError:
806+
return ""
807+
808+
@logging_formatter_str.setter # type: ignore[no-redef]
809+
def logging_formatter_str(self, value):
810+
self.logging_formatter = value
811+
792812
logging_handlers = QtPropertyInit(
793813
'_logging_handlers', list, callback=init_logging_handlers, typ="QStringList"
794814
)

preditor/stream/console_handler.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ class LoggingLevelDescriptor(DefaultDescriptor):
3737
_level_conversion = logging.Handler()
3838

3939
def __set__(self, obj, value):
40-
if value is not None:
40+
if value is None:
41+
value = logging.NOTSET
42+
else:
4143
try:
4244
value = int(value)
4345
except ValueError:
@@ -57,6 +59,11 @@ class FormatterDescriptor(DefaultDescriptor):
5759
If a string is passed it will be cast into a Formatter instance.
5860
"""
5961

62+
def __init__(self, *, ident=None, default=None):
63+
if isinstance(default, str):
64+
default = logging.Formatter(default)
65+
super().__init__(ident=ident, default=default)
66+
6067
def __set__(self, obj, value):
6168
if isinstance(value, str):
6269
value = logging.Formatter(value)
@@ -65,28 +72,23 @@ def __set__(self, obj, value):
6572

6673
@dataclass
6774
class HandlerInfo:
68-
_default_format = (
69-
'%(levelname)s %(module)s.%(funcName)s line:%(lineno)d - %(message)s'
70-
)
7175
name: str
7276
level: LoggingLevelDescriptor = LoggingLevelDescriptor()
7377
plugin: Optional[str] = "Console"
74-
formatter: FormatterDescriptor = FormatterDescriptor(default=_default_format)
78+
formatter: FormatterDescriptor = FormatterDescriptor()
7579

7680
_attr_names = {"plug": "plugin", "fmt": "formatter", "lvl": "level"}
7781

7882
def __post_init__(self):
79-
# Process self.name as a string if level is not defined
80-
if self.level is None:
81-
# Clear self.name so you can define omit name to define a root logger
82-
# For example passing "level=INFO" would set the root logger to info.
83-
name = self.name
84-
self.name = ""
85-
86-
parts = self.__to_parts__(name)
87-
for i, value in enumerate(parts):
88-
key, _value = self.__parse_setting__(value, i)
89-
setattr(self, key, _value)
83+
# Clear self.name so you can define omit name to define a root logger
84+
# For example passing "level=INFO" would set the root logger to info.
85+
name = self.name
86+
self.name = ""
87+
88+
parts = self.__to_parts__(name)
89+
for i, value in enumerate(parts):
90+
key, _value = self.__parse_setting__(value, i)
91+
setattr(self, key, _value)
9092

9193
@classmethod
9294
def __to_parts__(cls, value):

tests/test_stream.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
import sys
66
import traceback
7+
from logging import NOTSET
78

89
import pytest
910

@@ -236,19 +237,22 @@ def test_install_to_std():
236237
@pytest.mark.parametrize(
237238
"input,check",
238239
(
239-
(["preditor.cli"], ["preditor.cli", None, "Console", None]),
240+
(["preditor.cli"], ["preditor.cli", NOTSET, "Console", None]),
240241
(["preditor.cli,INFO"], ["preditor.cli", 20, "Console", None]),
241242
(["preditor.prefs,30"], ["preditor.prefs", 30, "Console", None]),
242243
(["preditor.cli,lvl=30"], ["preditor.cli", 30, "Console", None]),
243244
(["preditor.cli,level=WARNING"], ["preditor.cli", 30, "Console", None]),
244245
# Test escaping and complex formatter strings
245-
(["preditor,formatter=%(msg)s"], ["preditor", None, "Console", "%(msg)s"]),
246-
(["preditor,fmt=%(msg)s,POST"], ["preditor", None, "Console", "%(msg)s,POST"]),
247-
([r"preditor,fmt=M\=%(msg)s"], ["preditor", None, "Console", "M=%(msg)s"]),
248-
([r"preditor,fmt=M\\=%(msg)s"], ["preditor", None, "Console", r"M\=%(msg)s"]),
246+
(["preditor,formatter=%(msg)s"], ["preditor", NOTSET, "Console", "%(msg)s"]),
247+
(
248+
["preditor,fmt=%(msg)s,POST"],
249+
["preditor", NOTSET, "Console", "%(msg)s,POST"],
250+
),
251+
([r"preditor,fmt=M\=%(msg)s"], ["preditor", NOTSET, "Console", "M=%(msg)s"]),
252+
([r"preditor,fmt=M\\=%(msg)s"], ["preditor", NOTSET, "Console", r"M\=%(msg)s"]),
249253
(
250254
[r"preditor,fmt=M\=%(msg)s,POST"],
251-
["preditor", None, "Console", "M=%(msg)s,POST"],
255+
["preditor", NOTSET, "Console", "M=%(msg)s,POST"],
252256
),
253257
(
254258
[r"plug=PrEditor,fmt=A\=%(msg)sG\=G,level=WARNING,name=six"],
@@ -272,11 +276,11 @@ def test_handler_info(input, check):
272276
assert hi.name == check[0]
273277
assert hi.level == check[1]
274278
assert hi.plugin == check[2]
275-
assert isinstance(hi.formatter, logging.Formatter)
276279
if check[3] is None:
277-
# Treat None as the default formatter
278-
check[3] = HandlerInfo._default_format
279-
assert hi.formatter._fmt == check[3]
280+
assert hi.formatter is None
281+
else:
282+
assert isinstance(hi.formatter, logging.Formatter)
283+
assert hi.formatter._fmt == check[3]
280284

281285

282286
class TestShellPrint:

0 commit comments

Comments
 (0)