Merge pull request #2186 from nicoddemus/pytest-plugins-env-rewrite

Consider plugins loaded by PYTEST_PLUGINS for assertion rewrite
This commit is contained in:
Bruno Oliveira 2017-01-12 23:29:58 -02:00 committed by GitHub
commit 3dfdbaf490
4 changed files with 61 additions and 12 deletions

View File

@ -6,6 +6,10 @@
* pytest no longer recognizes coroutine functions as yield tests (`#2129`_). * pytest no longer recognizes coroutine functions as yield tests (`#2129`_).
Thanks to `@malinoff`_ for the PR. Thanks to `@malinoff`_ for the PR.
* Plugins loaded by the ``PYTEST_PLUGINS`` environment variable are now automatically
considered for assertion rewriting (`#2185`_).
Thanks `@nicoddemus`_ for the PR.
* Improve error message when pytest.warns fails (`#2150`_). The type(s) of the * Improve error message when pytest.warns fails (`#2150`_). The type(s) of the
expected warnings and the list of caught warnings is added to the expected warnings and the list of caught warnings is added to the
error message. Thanks `@lesteve`_ for the PR. error message. Thanks `@lesteve`_ for the PR.
@ -23,6 +27,7 @@
.. _#2129: https://github.com/pytest-dev/pytest/issues/2129 .. _#2129: https://github.com/pytest-dev/pytest/issues/2129
.. _#2148: https://github.com/pytest-dev/pytest/issues/2148 .. _#2148: https://github.com/pytest-dev/pytest/issues/2148
.. _#2150: https://github.com/pytest-dev/pytest/issues/2150 .. _#2150: https://github.com/pytest-dev/pytest/issues/2150
.. _#2185: https://github.com/pytest-dev/pytest/issues/2185
3.0.5 (2016-12-05) 3.0.5 (2016-12-05)

View File

@ -402,31 +402,26 @@ class PytestPluginManager(PluginManager):
self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
def consider_module(self, mod): def consider_module(self, mod):
plugins = getattr(mod, 'pytest_plugins', []) self._import_plugin_specs(getattr(mod, 'pytest_plugins', []))
if isinstance(plugins, str):
plugins = [plugins]
self.rewrite_hook.mark_rewrite(*plugins)
self._import_plugin_specs(plugins)
def _import_plugin_specs(self, spec): def _import_plugin_specs(self, spec):
if spec: plugins = _get_plugin_specs_as_list(spec)
if isinstance(spec, str): for import_spec in plugins:
spec = spec.split(",") self.import_plugin(import_spec)
for import_spec in spec:
self.import_plugin(import_spec)
def import_plugin(self, modname): def import_plugin(self, modname):
# most often modname refers to builtin modules, e.g. "pytester", # most often modname refers to builtin modules, e.g. "pytester",
# "terminal" or "capture". Those plugins are registered under their # "terminal" or "capture". Those plugins are registered under their
# basename for historic purposes but must be imported with the # basename for historic purposes but must be imported with the
# _pytest prefix. # _pytest prefix.
assert isinstance(modname, str) assert isinstance(modname, str), "module name as string required, got %r" % modname
if self.get_plugin(modname) is not None: if self.get_plugin(modname) is not None:
return return
if modname in builtin_plugins: if modname in builtin_plugins:
importspec = "_pytest." + modname importspec = "_pytest." + modname
else: else:
importspec = modname importspec = modname
self.rewrite_hook.mark_rewrite(modname)
try: try:
__import__(importspec) __import__(importspec)
except ImportError as e: except ImportError as e:
@ -447,6 +442,24 @@ class PytestPluginManager(PluginManager):
self.consider_module(mod) self.consider_module(mod)
def _get_plugin_specs_as_list(specs):
"""
Parses a list of "plugin specs" and returns a list of plugin names.
Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in
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):
specs = specs.split(',') if specs else []
if not isinstance(specs, (list, tuple)):
raise UsageError("Plugin specs must be a ','-separated string or a "
"list/tuple of strings for plugin names. Given: %r" % specs)
return list(specs)
return []
class Parser: class Parser:
""" Parser for command line arguments and ini-file values. """ Parser for command line arguments and ini-file values.

View File

@ -682,7 +682,7 @@ def test_rewritten():
hook.mark_rewrite('test_remember_rewritten_modules') hook.mark_rewrite('test_remember_rewritten_modules')
assert warnings == [] assert warnings == []
def test_rewrite_warning_using_pytest_plugins(self, testdir, monkeypatch): def test_rewrite_warning_using_pytest_plugins(self, testdir):
testdir.makepyfile(**{ testdir.makepyfile(**{
'conftest.py': "pytest_plugins = ['core', 'gui', 'sci']", 'conftest.py': "pytest_plugins = ['core', 'gui', 'sci']",
'core.py': "", 'core.py': "",
@ -695,6 +695,22 @@ def test_rewritten():
result.stdout.fnmatch_lines(['*= 1 passed in *=*']) result.stdout.fnmatch_lines(['*= 1 passed in *=*'])
assert 'pytest-warning summary' not in result.stdout.str() assert 'pytest-warning summary' not in result.stdout.str()
def test_rewrite_warning_using_pytest_plugins_env_var(self, testdir, monkeypatch):
monkeypatch.setenv('PYTEST_PLUGINS', 'plugin')
testdir.makepyfile(**{
'plugin.py': "",
'test_rewrite_warning_using_pytest_plugins_env_var.py': """
import plugin
pytest_plugins = ['plugin']
def test():
pass
""",
})
testdir.chdir()
result = testdir.runpytest_subprocess()
result.stdout.fnmatch_lines(['*= 1 passed in *=*'])
assert 'pytest-warning summary' not in result.stdout.str()
class TestAssertionRewriteHookDetails(object): class TestAssertionRewriteHookDetails(object):
def test_loader_is_package_false_for_module(self, testdir): def test_loader_is_package_false_for_module(self, testdir):

View File

@ -587,6 +587,21 @@ def test_load_initial_conftest_last_ordering(testdir):
assert [x.function.__module__ for x in l] == expected assert [x.function.__module__ for x in l] == expected
def test_get_plugin_specs_as_list():
from _pytest.config import _get_plugin_specs_as_list
with pytest.raises(pytest.UsageError):
_get_plugin_specs_as_list(set(['foo']))
with pytest.raises(pytest.UsageError):
_get_plugin_specs_as_list(dict())
assert _get_plugin_specs_as_list(None) == []
assert _get_plugin_specs_as_list('') == []
assert _get_plugin_specs_as_list('foo') == ['foo']
assert _get_plugin_specs_as_list('foo,bar') == ['foo', 'bar']
assert _get_plugin_specs_as_list(['foo', 'bar']) == ['foo', 'bar']
assert _get_plugin_specs_as_list(('foo', 'bar')) == ['foo', 'bar']
class TestWarning: class TestWarning:
def test_warn_config(self, testdir): def test_warn_config(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""