Merge master into features

This commit is contained in:
Daniel Hahler
2019-02-08 20:09:09 +01:00
29 changed files with 321 additions and 103 deletions

View File

@@ -559,8 +559,8 @@ def _get_plugin_specs_as_list(specs):
which case it is returned as a list. Specs can also be `None` in which case an
empty list is returned.
"""
if specs is not None:
if isinstance(specs, str):
if specs is not None and not isinstance(specs, types.ModuleType):
if isinstance(specs, six.string_types):
specs = specs.split(",") if specs else []
if not isinstance(specs, (list, tuple)):
raise UsageError(

View File

@@ -370,6 +370,8 @@ def get_actual_log_level(config, *setting_names):
)
# run after terminalreporter/capturemanager are configured
@pytest.hookimpl(trylast=True)
def pytest_configure(config):
config.pluginmanager.register(LoggingPlugin(config), "logging-plugin")
@@ -388,8 +390,6 @@ class LoggingPlugin(object):
# enable verbose output automatically if live logging is enabled
if self._log_cli_enabled() and not config.getoption("verbose"):
# sanity check: terminal reporter should not have been loaded at this point
assert self._config.pluginmanager.get_plugin("terminalreporter") is None
config.option.verbose = 1
self.print_logs = get_option_ini(config, "log_print")
@@ -420,6 +420,54 @@ class LoggingPlugin(object):
self.log_cli_handler = None
self.live_logs_context = lambda: dummy_context_manager()
# Note that the lambda for the live_logs_context is needed because
# live_logs_context can otherwise not be entered multiple times due
# to limitations of contextlib.contextmanager.
if self._log_cli_enabled():
self._setup_cli_logging()
def _setup_cli_logging(self):
config = self._config
terminal_reporter = config.pluginmanager.get_plugin("terminalreporter")
if terminal_reporter is None:
# terminal reporter is disabled e.g. by pytest-xdist.
return
# FIXME don't set verbosity level and derived attributes of
# terminalwriter directly
terminal_reporter.verbosity = config.option.verbose
terminal_reporter.showheader = terminal_reporter.verbosity >= 0
terminal_reporter.showfspath = terminal_reporter.verbosity >= 0
terminal_reporter.showlongtestinfo = terminal_reporter.verbosity > 0
capture_manager = config.pluginmanager.get_plugin("capturemanager")
# if capturemanager plugin is disabled, live logging still works.
log_cli_handler = _LiveLoggingStreamHandler(terminal_reporter, capture_manager)
log_cli_format = get_option_ini(config, "log_cli_format", "log_format")
log_cli_date_format = get_option_ini(
config, "log_cli_date_format", "log_date_format"
)
if (
config.option.color != "no"
and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search(log_cli_format)
):
log_cli_formatter = ColoredLevelFormatter(
create_terminal_writer(config),
log_cli_format,
datefmt=log_cli_date_format,
)
else:
log_cli_formatter = logging.Formatter(
log_cli_format, datefmt=log_cli_date_format
)
log_cli_level = get_actual_log_level(config, "log_cli_level", "log_level")
self.log_cli_handler = log_cli_handler
self.live_logs_context = lambda: catching_logs(
log_cli_handler, formatter=log_cli_formatter, level=log_cli_level
)
def _log_cli_enabled(self):
"""Return True if log_cli should be considered enabled, either explicitly
or because --log-cli-level was given in the command-line.
@@ -430,10 +478,6 @@ class LoggingPlugin(object):
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection(self):
# This has to be called before the first log message is logged,
# so we can access the terminal reporter plugin.
self._setup_cli_logging()
with self.live_logs_context():
if self.log_cli_handler:
self.log_cli_handler.set_when("collection")
@@ -513,7 +557,6 @@ class LoggingPlugin(object):
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_sessionstart(self):
self._setup_cli_logging()
with self.live_logs_context():
if self.log_cli_handler:
self.log_cli_handler.set_when("sessionstart")
@@ -533,46 +576,6 @@ class LoggingPlugin(object):
else:
yield # run all the tests
def _setup_cli_logging(self):
"""Sets up the handler and logger for the Live Logs feature, if enabled."""
terminal_reporter = self._config.pluginmanager.get_plugin("terminalreporter")
if self._log_cli_enabled() and terminal_reporter is not None:
capture_manager = self._config.pluginmanager.get_plugin("capturemanager")
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"
)
if (
self._config.option.color != "no"
and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search(log_cli_format)
):
log_cli_formatter = ColoredLevelFormatter(
create_terminal_writer(self._config),
log_cli_format,
datefmt=log_cli_date_format,
)
else:
log_cli_formatter = logging.Formatter(
log_cli_format, datefmt=log_cli_date_format
)
log_cli_level = get_actual_log_level(
self._config, "log_cli_level", "log_level"
)
self.log_cli_handler = log_cli_handler
self.live_logs_context = lambda: catching_logs(
log_cli_handler, formatter=log_cli_formatter, level=log_cli_level
)
else:
self.live_logs_context = lambda: dummy_context_manager()
# Note that the lambda for the live_logs_context is needed because
# live_logs_context can otherwise not be entered multiple times due
# to limitations of contextlib.contextmanager
class _LiveLoggingStreamHandler(logging.StreamHandler):
"""

View File

@@ -1144,9 +1144,10 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
def _idval(val, argname, idx, idfn, item, config):
if idfn:
s = None
try:
s = idfn(val)
generated_id = idfn(val)
if generated_id is not None:
val = generated_id
except Exception as e:
# See issue https://github.com/pytest-dev/pytest/issues/2169
msg = "{}: error raised while trying to determine id of parameter '{}' at position {}\n"
@@ -1154,10 +1155,7 @@ def _idval(val, argname, idx, idfn, item, config):
# we only append the exception type and message because on Python 2 reraise does nothing
msg += " {}: {}\n".format(type(e).__name__, e)
six.raise_from(ValueError(msg), e)
if s:
return ascii_escaped(s)
if config:
elif config:
hook_id = config.hook.pytest_make_parametrize_id(
config=config, val=val, argname=argname
)

View File

@@ -621,6 +621,14 @@ def raises(expected_exception, *args, **kwargs):
...
>>> assert exc_info.type is ValueError
**Using with** ``pytest.mark.parametrize``
When using :ref:`pytest.mark.parametrize ref`
it is possible to parametrize tests such that
some runs raise an exception and others do not.
See :ref:`parametrizing_conditional_raising` for an example.
**Legacy form**
It is possible to specify a callable by passing a to-be-called lambda::

View File

@@ -574,19 +574,20 @@ class TerminalReporter(object):
return lines
def pytest_collection_finish(self, session):
if self.config.option.collectonly:
if self.config.getoption("collectonly"):
self._printcollecteditems(session.items)
if self.stats.get("failed"):
self._tw.sep("!", "collection failures")
for rep in self.stats.get("failed"):
rep.toterminal(self._tw)
return 1
return 0
lines = self.config.hook.pytest_report_collectionfinish(
config=self.config, startdir=self.startdir, items=session.items
)
self._write_report_lines_from_hooks(lines)
if self.config.getoption("collectonly"):
if self.stats.get("failed"):
self._tw.sep("!", "collection failures")
for rep in self.stats.get("failed"):
rep.toterminal(self._tw)
def _printcollecteditems(self, items):
# to print out items and their parent collectors
# we take care to leave out Instances aka ()

View File

@@ -87,6 +87,9 @@ def _make_xunit_fixture(obj, setup_name, teardown_name, scope, pass_self):
@pytest.fixture(scope=scope, autouse=True)
def fixture(self, request):
if getattr(self, "__unittest_skip__", None):
reason = self.__unittest_skip_why__
pytest.skip(reason)
if setup is not None:
if pass_self:
setup(self, request.function)