diff options
author | Daniel Hahler <git@thequod.de> | 2019-11-06 00:35:24 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-06 00:35:24 +0100 |
commit | e7320c6b547750e630ac59d8b40d48be25e779ce (patch) | |
tree | bccb01bc864aebe8c9b66e08921b776d979a1d9c /src/_pytest/logging.py | |
parent | 00f67494e5de70ffa4427bf0802a164c5790160d (diff) | |
parent | 1f5b454355d27bccda2ce1b3b145cfbde020fb76 (diff) | |
download | pytest-e7320c6b547750e630ac59d8b40d48be25e779ce.tar.gz |
Merge pull request #5926 from AtakamaLLC/optional-multiline
Add log-auto-indent option to control multiline formatting
Diffstat (limited to 'src/_pytest/logging.py')
-rw-r--r-- | src/_pytest/logging.py | 99 |
1 files changed, 87 insertions, 12 deletions
diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index dc45227b2..ccd79b834 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -10,6 +10,7 @@ from typing import Mapping import pytest from _pytest.compat import nullcontext +from _pytest.config import _strtobool from _pytest.config import create_terminal_writer from _pytest.pathlib import Path @@ -76,24 +77,87 @@ class PercentStyleMultiline(logging.PercentStyle): formats the message as if each line were logged separately. """ + def __init__(self, fmt, auto_indent): + super().__init__(fmt) + self._auto_indent = self._get_auto_indent(auto_indent) + @staticmethod def _update_message(record_dict, message): tmp = record_dict.copy() tmp["message"] = message return tmp + @staticmethod + def _get_auto_indent(auto_indent_option) -> int: + """Determines the current auto indentation setting + + Specify auto indent behavior (on/off/fixed) by passing in + extra={"auto_indent": [value]} to the call to logging.log() or + using a --log-auto-indent [value] command line or the + log_auto_indent [value] config option. + + Default behavior is auto-indent off. + + Using the string "True" or "on" or the boolean True as the value + turns auto indent on, using the string "False" or "off" or the + boolean False or the int 0 turns it off, and specifying a + positive integer fixes the indentation position to the value + specified. + + Any other values for the option are invalid, and will silently be + converted to the default. + + :param any auto_indent_option: User specified option for indentation + from command line, config or extra kwarg. Accepts int, bool or str. + str option accepts the same range of values as boolean config options, + as well as positive integers represented in str form. + + :returns: indentation value, which can be + -1 (automatically determine indentation) or + 0 (auto-indent turned off) or + >0 (explicitly set indentation position). + """ + + if type(auto_indent_option) is int: + return int(auto_indent_option) + elif type(auto_indent_option) is str: + try: + return int(auto_indent_option) + except ValueError: + pass + try: + if _strtobool(auto_indent_option): + return -1 + except ValueError: + return 0 + elif type(auto_indent_option) is bool: + if auto_indent_option: + return -1 + + return 0 + def format(self, record): if "\n" in record.message: - lines = record.message.splitlines() - formatted = self._fmt % self._update_message(record.__dict__, lines[0]) - # TODO optimize this by introducing an option that tells the - # logging framework that the indentation doesn't - # change. This allows to compute the indentation only once. - indentation = _remove_ansi_escape_sequences(formatted).find(lines[0]) - lines[0] = formatted - return ("\n" + " " * indentation).join(lines) - else: - return self._fmt % record.__dict__ + if hasattr(record, "auto_indent"): + # passed in from the "extra={}" kwarg on the call to logging.log() + auto_indent = self._get_auto_indent(record.auto_indent) + else: + auto_indent = self._auto_indent + + if auto_indent: + lines = record.message.splitlines() + formatted = self._fmt % self._update_message(record.__dict__, lines[0]) + + if auto_indent < 0: + indentation = _remove_ansi_escape_sequences(formatted).find( + lines[0] + ) + else: + # optimizes logging by allowing a fixed indentation + indentation = auto_indent + lines[0] = formatted + return ("\n" + " " * indentation).join(lines) + return self._fmt % record.__dict__ def get_option_ini(config, *names): @@ -187,6 +251,12 @@ def pytest_addoption(parser): default=DEFAULT_LOG_DATE_FORMAT, help="log date format as used by the logging module.", ) + add_option_ini( + "--log-auto-indent", + dest="log_auto_indent", + default=None, + help="Auto-indent multiline messages passed to the logging module. Accepts true|on, false|off or an integer.", + ) @contextmanager @@ -418,6 +488,7 @@ class LoggingPlugin: self.formatter = self._create_formatter( get_option_ini(config, "log_format"), get_option_ini(config, "log_date_format"), + get_option_ini(config, "log_auto_indent"), ) self.log_level = get_actual_log_level(config, "log_level") @@ -449,7 +520,7 @@ class LoggingPlugin: if self._log_cli_enabled(): self._setup_cli_logging() - def _create_formatter(self, log_format, log_date_format): + def _create_formatter(self, log_format, log_date_format, auto_indent): # color option doesn't exist if terminal plugin is disabled color = getattr(self._config.option, "color", "no") if color != "no" and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search( @@ -461,7 +532,10 @@ class LoggingPlugin: else: formatter = logging.Formatter(log_format, log_date_format) - formatter._style = PercentStyleMultiline(formatter._style._fmt) + formatter._style = PercentStyleMultiline( + formatter._style._fmt, auto_indent=auto_indent + ) + return formatter def _setup_cli_logging(self): @@ -478,6 +552,7 @@ class LoggingPlugin: log_cli_formatter = self._create_formatter( get_option_ini(config, "log_cli_format", "log_format"), get_option_ini(config, "log_cli_date_format", "log_date_format"), + get_option_ini(config, "log_auto_indent"), ) log_cli_level = get_actual_log_level(config, "log_cli_level", "log_level") |