Skip to content

Commit bdf87f2

Browse files
committed
Live log option now writes to the terminal reporter
Ref: pytest-dev#3013
1 parent 1b9f427 commit bdf87f2

File tree

2 files changed

+44
-33
lines changed

2 files changed

+44
-33
lines changed

_pytest/logging.py

+26-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import logging
44
from contextlib import closing, contextmanager
5-
import sys
65
import six
76

87
import pytest
@@ -263,30 +262,13 @@ def __init__(self, config):
263262
The formatter can be safely shared across all handlers so
264263
create a single one for the entire test session here.
265264
"""
265+
self._config = config
266266
self.print_logs = get_option_ini(config, 'log_print')
267267
self.formatter = logging.Formatter(
268268
get_option_ini(config, 'log_format'),
269269
get_option_ini(config, 'log_date_format'))
270270
self.log_level = get_actual_log_level(config, 'log_level')
271271

272-
if config.getini('log_cli'):
273-
log_cli_handler = logging.StreamHandler(sys.stderr)
274-
log_cli_format = get_option_ini(
275-
config, 'log_cli_format', 'log_format')
276-
log_cli_date_format = get_option_ini(
277-
config, 'log_cli_date_format', 'log_date_format')
278-
log_cli_formatter = logging.Formatter(
279-
log_cli_format,
280-
datefmt=log_cli_date_format)
281-
log_cli_level = get_actual_log_level(config, 'log_cli_level', 'log_level')
282-
self.log_cli_handler = log_cli_handler # needed for a single unittest
283-
self.live_logs_context = catching_logs(log_cli_handler,
284-
formatter=log_cli_formatter,
285-
level=log_cli_level)
286-
else:
287-
self.log_cli_handler = None
288-
self.live_logs_context = _dummy_context_manager()
289-
290272
log_file = get_option_ini(config, 'log_file')
291273
if log_file:
292274
self.log_file_level = get_actual_log_level(config, 'log_file_level')
@@ -340,6 +322,7 @@ def pytest_runtest_teardown(self, item):
340322
@pytest.hookimpl(hookwrapper=True)
341323
def pytest_runtestloop(self, session):
342324
"""Runs all collected test items."""
325+
self._setup_cli_logging()
343326
with self.live_logs_context:
344327
if self.log_file_handler is not None:
345328
with closing(self.log_file_handler):
@@ -348,3 +331,27 @@ def pytest_runtestloop(self, session):
348331
yield # run all the tests
349332
else:
350333
yield # run all the tests
334+
335+
def _setup_cli_logging(self):
336+
"""Setups the handler and logger for the Live Logs feature, if enabled.
337+
338+
This must be done right before starting the loop so we can access the terminal reporter plugin.
339+
"""
340+
terminal_reporter = self._config.pluginmanager.get_plugin('terminalreporter')
341+
if self._config.getini('log_cli') and terminal_reporter is not None:
342+
log_cli_handler = logging.StreamHandler(terminal_reporter._tw)
343+
log_cli_format = get_option_ini(
344+
self._config, 'log_cli_format', 'log_format')
345+
log_cli_date_format = get_option_ini(
346+
self._config, 'log_cli_date_format', 'log_date_format')
347+
log_cli_formatter = logging.Formatter(
348+
log_cli_format,
349+
datefmt=log_cli_date_format)
350+
log_cli_level = get_actual_log_level(self._config, 'log_cli_level', 'log_level')
351+
self.log_cli_handler = log_cli_handler # needed for a single unittest
352+
self.live_logs_context = catching_logs(log_cli_handler,
353+
formatter=log_cli_formatter,
354+
level=log_cli_level)
355+
else:
356+
self.log_cli_handler = None
357+
self.live_logs_context = _dummy_context_manager()

testing/logging/test_reporting.py

+18-14
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,9 @@ def test_log_cli():
156156
''')
157157
result = testdir.runpytest('-s')
158158
if enabled:
159-
assert msg in result.stderr.str()
159+
assert msg in result.stdout.str()
160160
else:
161-
assert msg not in result.stderr.str()
161+
assert msg not in result.stdout.str()
162162

163163

164164
def test_log_cli_default_level(testdir):
@@ -182,12 +182,13 @@ def test_log_cli(request):
182182

183183
# fnmatch_lines does an assertion internally
184184
result.stdout.fnmatch_lines([
185-
'test_log_cli_default_level.py PASSED',
185+
'test_log_cli_default_level.py*',
186+
'PASSED', # 'PASSED' on its own line because the log message prints a new line
186187
])
187-
result.stderr.fnmatch_lines([
188+
result.stdout.fnmatch_lines([
188189
'*WARNING message will be shown*',
189190
])
190-
assert "INFO message won't be shown" not in result.stderr.str()
191+
assert "INFO message won't be shown" not in result.stdout.str()
191192
# make sure that that we get a '0' exit code for the testsuite
192193
assert result.ret == 0
193194

@@ -213,12 +214,13 @@ def test_log_cli(request):
213214

214215
# fnmatch_lines does an assertion internally
215216
result.stdout.fnmatch_lines([
216-
'test_log_cli_level.py PASSED',
217+
'test_log_cli_level.py*',
218+
'PASSED', # 'PASSED' on its own line because the log message prints a new line
217219
])
218-
result.stderr.fnmatch_lines([
220+
result.stdout.fnmatch_lines([
219221
"* This log message will be shown"
220222
])
221-
for line in result.errlines:
223+
for line in result.outlines:
222224
try:
223225
assert "This log message won't be shown" in line
224226
pytest.fail("A log message was shown and it shouldn't have been")
@@ -232,12 +234,13 @@ def test_log_cli(request):
232234

233235
# fnmatch_lines does an assertion internally
234236
result.stdout.fnmatch_lines([
235-
'test_log_cli_level.py PASSED',
237+
'test_log_cli_level.py*',
238+
'PASSED', # 'PASSED' on its own line because the log message prints a new line
236239
])
237-
result.stderr.fnmatch_lines([
240+
result.stdout.fnmatch_lines([
238241
"* This log message will be shown"
239242
])
240-
for line in result.errlines:
243+
for line in result.outlines:
241244
try:
242245
assert "This log message won't be shown" in line
243246
pytest.fail("A log message was shown and it shouldn't have been")
@@ -270,12 +273,13 @@ def test_log_cli(request):
270273

271274
# fnmatch_lines does an assertion internally
272275
result.stdout.fnmatch_lines([
273-
'test_log_cli_ini_level.py PASSED',
276+
'test_log_cli_ini_level.py*',
277+
'PASSED', # 'PASSED' on its own line because the log message prints a new line
274278
])
275-
result.stderr.fnmatch_lines([
279+
result.stdout.fnmatch_lines([
276280
"* This log message will be shown"
277281
])
278-
for line in result.errlines:
282+
for line in result.outlines:
279283
try:
280284
assert "This log message won't be shown" in line
281285
pytest.fail("A log message was shown and it shouldn't have been")

0 commit comments

Comments
 (0)