Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Colorize live-log levelnames #3142

Merged
merged 1 commit into from Jan 30, 2018

Conversation

@thisch
Copy link
Contributor

@thisch thisch commented Jan 23, 2018

I propose to prettify the default output of the live logs a bit by colorizing the levelnames.

A screenshot paints a thousand words:
image

@thisch thisch changed the base branch from master to features Jan 23, 2018
@thisch thisch force-pushed the only_colorize_levelname branch from c7d729c to 216b21a Jan 23, 2018
@coveralls
Copy link

@coveralls coveralls commented Jan 23, 2018

Coverage Status

Coverage decreased (-0.02%) to 92.628% when pulling ebab1b6 on thisch:only_colorize_levelname into 49773b5 on pytest-dev:features.

@thisch thisch force-pushed the only_colorize_levelname branch 2 times, most recently from 87013c4 to d797e3f Jan 26, 2018
@thisch thisch changed the title WIP: Colorize live-log levelnames Colorize live-log levelnames Jan 26, 2018
@thisch thisch force-pushed the only_colorize_levelname branch from d797e3f to ed1051f Jan 26, 2018
@thisch
Copy link
Contributor Author

@thisch thisch commented Jan 26, 2018

What do you think about this?

Are there tests that test the text colors of the pytest output, where I can take a look at?

@nicoddemus
Copy link
Member

@nicoddemus nicoddemus commented Jan 26, 2018

Are there tests that test the text colors of the pytest output, where I can take a look at?

Unfortunately no AFAIK. 😕

@thisch
Copy link
Contributor Author

@thisch thisch commented Jan 26, 2018

Note that I successfully tested this PR in cpython2.7 and in cpython3.6.

@nicoddemus
Copy link
Member

@nicoddemus nicoddemus commented Jan 27, 2018

This is how it looks on Windows:

colored-log

👍

Copy link
Member

@nicoddemus nicoddemus left a comment

This looks great @thisch, please take a look at my comments! 👍

DEFAULT_LOG_FORMAT = '%(filename)-25s %(lineno)4d %(levelname)-8s %(message)s'
DEFAULT_LOG_DATE_FORMAT = '%H:%M:%S'

# move to __init__ of ColoredLevelFormatter?
LEVELNAME_FMT_REGEX = re.compile(r'%\(levelname\)([+-]?\d*s)')
Copy link
Member

@nicoddemus nicoddemus Jan 27, 2018

Probably both LEVELNAME_FMT_REGEX and LOGLEVEL_COLOROPTS would be better off being class attributes of ColoredLevelFormatter.

def __init__(self, *args, **kwargs):
super(ColoredLevelFormatter, self).__init__(
*args, **kwargs)
if py.std.sys.version_info[0] >= 3:
Copy link
Member

@nicoddemus nicoddemus Jan 27, 2018

Please use six.PY3 instead (this will make it easier later to find python2-specific code)

fmt = self._level_to_fmt_mapping.get(
record.levelno, self._original_fmt)

if py.std.sys.version_info[0] >= 3:
Copy link
Member

@nicoddemus nicoddemus Jan 27, 2018

Ditto here for six.PY3 👍

LEVELNAME_FMT_REGEX = re.compile(r'%\(levelname\)([+-]?\d*s)')


class ColoredLevelFormatter(logging.Formatter):
Copy link
Member

@nicoddemus nicoddemus Jan 27, 2018

To test this, probably the way to go is to unittest the ColoredLevelFormatter class directly: create an instance and pass different LogRecord instances and ensure they have the correct markup applied. Also interesting to use custom log levels and ensure they don't have any markup.

Copy link
Contributor Author

@thisch thisch Jan 27, 2018

Unfortunately this does not work, since tw.markup() does not add ANSI escape sequences around the string in the unit tests:

(Pdb++) tw.markup('abc', red=True)
'abc'
(Pdb++) tw.hasmarkup
False

Any ideas?

Copy link
Contributor Author

@thisch thisch Jan 27, 2018

I'll do some mocking and then it should work.

Copy link
Contributor Author

@thisch thisch Jan 30, 2018

@thisch thisch force-pushed the only_colorize_levelname branch from ed1051f to c1da614 Jan 27, 2018
@thisch
Copy link
Contributor Author

@thisch thisch commented Jan 30, 2018

I guess that this is already too late for pytest 3.4, right?

@thisch
Copy link
Contributor Author

@thisch thisch commented Jan 30, 2018

BTW, I don't understand why the tests fail.

@nicoddemus
Copy link
Member

@nicoddemus nicoddemus commented Jan 30, 2018

I guess that this is already too late for pytest 3.4, right?

I plan to release 3.4 when I get home later, but if we merge this until then I will be happy to generate the package again.

BTW, I don't understand why the tests fail.

No idea either, seems to be some bad interaction with mock... I suggest to try to use monkeypatch in your test instead of mock to see if the problem goes away (plus it will work on Python 2 as well).

@thisch thisch force-pushed the only_colorize_levelname branch from c1da614 to 6c0a8ac Jan 30, 2018
@thisch
Copy link
Contributor Author

@thisch thisch commented Jan 30, 2018

Let's hope that the tests now pass.

@nicoddemus
Copy link
Member

@nicoddemus nicoddemus commented Jan 30, 2018

Just remembered this now and would like to confirm, passing --color=no disables the colors?

@thisch thisch force-pushed the only_colorize_levelname branch from 6c0a8ac to 1ad52b9 Jan 30, 2018
@thisch
Copy link
Contributor Author

@thisch thisch commented Jan 30, 2018

Now the color config option should be taken into account. This made the monkeypatching code obsolete.

@@ -376,7 +432,10 @@ def _setup_cli_logging(self):
log_cli_handler = _LiveLoggingStreamHandler(terminal_reporter, capture_manager)
log_cli_format = get_option_ini(self._config, 'log_cli_format', 'log_format')
log_cli_date_format = get_option_ini(self._config, 'log_cli_date_format', 'log_date_format')
log_cli_formatter = logging.Formatter(log_cli_format, datefmt=log_cli_date_format)
if ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search(log_cli_format):
log_cli_formatter = ColoredLevelFormatter(self._config, log_cli_format, datefmt=log_cli_date_format)
Copy link
Contributor Author

@thisch thisch Jan 30, 2018

I could also perform the color check in the condition in line 435. Then I don't need to pass self._config to the __init__ method of ColoredLevelFormatter.

Copy link
Member

@nicoddemus nicoddemus Jan 30, 2018

Seems better

Copy link
Contributor Author

@thisch thisch Jan 30, 2018

I didn't know that color is a ternary type ('yes', 'no' and 'auto'). So I can't add self._config.option.color == 'yes' and to the if condition. If the value of the color option is 'auto' the TerminalWriter.init determines if the terminal supports markup or not.

An alternative would be to pass a terminalwriter object (_pytest.config.create_terminal_writer(self._config)) as the first arg of ColoredLevelFormatter. Don't know if this makes it better.

Copy link
Member

@nicoddemus nicoddemus Jan 30, 2018

An alternative would be to pass a terminalwriter object (_pytest.config.create_terminal_writer(self._config)) as the first arg of ColoredLevelFormatter. Don't know if this makes it better.

This reduces the number of dependencies at least (ColoredLevelFormatter will then depend only on TerminalWriter, instead of Config and TerminalWriter with the current code), so feel free to go with this route.

@thisch thisch force-pushed the only_colorize_levelname branch from 1ad52b9 to ebab1b6 Jan 30, 2018
@thisch
Copy link
Contributor Author

@thisch thisch commented Jan 30, 2018

If all tests pass, this is ready for merging.

@nicoddemus nicoddemus merged commit 71a7b3c into pytest-dev:features Jan 30, 2018
2 checks passed
@nicoddemus
Copy link
Member

@nicoddemus nicoddemus commented Jan 30, 2018

Thanks @thisch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

3 participants