adapt to pluggy naming, rename pytest.hookspec_opts to pytest.hookspec,s ame with hookimpl_opts

--HG--
branch : pluggy1
This commit is contained in:
holger krekel 2015-05-06 10:08:08 +02:00
parent 5ea7f0342b
commit bddc88f09e
20 changed files with 81 additions and 75 deletions

View File

@ -27,8 +27,8 @@
details like especially the pluginmanager.add_shutdown() API.
Thanks Holger Krekel.
- pluginmanagement: introduce ``pytest.hookimpl_opts`` and
``pytest.hookspec_opts`` decorators for setting impl/spec
- pluginmanagement: introduce ``pytest.hookimpl`` and
``pytest.hookspec`` decorators for setting impl/spec
specific parameters. This substitutes the previous
now deprecated use of ``pytest.mark`` which is meant to
contain markers for test functions only.

View File

@ -29,7 +29,7 @@ def pytest_addoption(parser):
help="shortcut for --capture=no.")
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_load_initial_conftests(early_config, parser, args):
ns = early_config.known_args_namespace
pluginmanager = early_config.pluginmanager
@ -101,7 +101,7 @@ class CaptureManager:
if capfuncarg is not None:
capfuncarg.close()
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_make_collect_report(self, collector):
if isinstance(collector, pytest.File):
self.resumecapture()
@ -115,13 +115,13 @@ class CaptureManager:
else:
yield
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_setup(self, item):
self.resumecapture()
yield
self.suspendcapture_item(item, "setup")
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(self, item):
self.resumecapture()
self.activate_funcargs(item)
@ -129,17 +129,17 @@ class CaptureManager:
#self.deactivate_funcargs() called from suspendcapture()
self.suspendcapture_item(item, "call")
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_teardown(self, item):
self.resumecapture()
yield
self.suspendcapture_item(item, "teardown")
@pytest.hookimpl_opts(tryfirst=True)
@pytest.hookimpl(tryfirst=True)
def pytest_keyboard_interrupt(self, excinfo):
self.reset_capturings()
@pytest.hookimpl_opts(tryfirst=True)
@pytest.hookimpl(tryfirst=True)
def pytest_internalerror(self, excinfo):
self.reset_capturings()

View File

@ -8,11 +8,11 @@ import warnings
import py
# DON't import pytest here because it causes import cycle troubles
import sys, os
from _pytest import hookspec # the extension point definitions
from pluggy import PluginManager, HookimplDecorator, HookspecDecorator
import _pytest.hookspec # the extension point definitions
from pluggy import PluginManager, HookimplMarker, HookspecMarker
hookimpl_opts = HookimplDecorator("pytest")
hookspec_opts = HookspecDecorator("pytest")
hookimpl = HookimplMarker("pytest")
hookspec = HookspecMarker("pytest")
# pytest startup
#
@ -121,7 +121,7 @@ class PytestPluginManager(PluginManager):
self._conftestpath2mod = {}
self._confcutdir = None
self.add_hookspecs(hookspec)
self.add_hookspecs(_pytest.hookspec)
self.register(self)
if os.environ.get('PYTEST_DEBUG'):
err = sys.stderr
@ -184,7 +184,7 @@ class PytestPluginManager(PluginManager):
return self.get_plugin(name)
def pytest_configure(self, config):
# XXX now that the pluginmanager exposes hookimpl_opts(tryfirst...)
# XXX now that the pluginmanager exposes hookimpl(tryfirst...)
# we should remove tryfirst/trylast as markers
config.addinivalue_line("markers",
"tryfirst: mark a hook implementation function such that the "
@ -827,7 +827,7 @@ class Config(object):
if not hasattr(self.option, opt.dest):
setattr(self.option, opt.dest, opt.default)
@hookimpl_opts(trylast=True)
@hookimpl(trylast=True)
def pytest_load_initial_conftests(self, early_config):
self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)

View File

@ -22,7 +22,7 @@ def pytest_addoption(parser):
help="store internal tracing debug information in 'pytestdebug.log'.")
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_cmdline_parse():
outcome = yield
config = outcome.get_result()

View File

@ -1,32 +1,32 @@
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
from pluggy import HookspecDecorator
from pluggy import HookspecMarker
hookspec_opts = HookspecDecorator("pytest")
hookspec = HookspecMarker("pytest")
# -------------------------------------------------------------------------
# Initialization hooks called for every plugin
# -------------------------------------------------------------------------
@hookspec_opts(historic=True)
@hookspec(historic=True)
def pytest_addhooks(pluginmanager):
"""called at plugin registration time to allow adding new hooks via a call to
pluginmanager.addhooks(module_or_class, prefix)."""
pluginmanager.add_hookspecs(module_or_class, prefix)."""
@hookspec_opts(historic=True)
@hookspec(historic=True)
def pytest_namespace():
"""return dict of name->object to be made globally available in
the pytest namespace. This hook is called at plugin registration
time.
"""
@hookspec_opts(historic=True)
@hookspec(historic=True)
def pytest_plugin_registered(plugin, manager):
""" a new pytest plugin got registered. """
@hookspec_opts(historic=True)
@hookspec(historic=True)
def pytest_addoption(parser):
"""register argparse-style options and ini-style config values.
@ -52,7 +52,7 @@ def pytest_addoption(parser):
via (deprecated) ``pytest.config``.
"""
@hookspec_opts(historic=True)
@hookspec(historic=True)
def pytest_configure(config):
""" called after command line options have been parsed
and all plugins and initial conftest files been loaded.
@ -65,14 +65,14 @@ def pytest_configure(config):
# discoverable conftest.py local plugins.
# -------------------------------------------------------------------------
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_cmdline_parse(pluginmanager, args):
"""return initialized config object, parsing the specified args. """
def pytest_cmdline_preparse(config, args):
"""(deprecated) modify command line arguments before option parsing. """
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_cmdline_main(config):
""" called for performing the main command line action. The default
implementation will invoke the configure hooks and runtest_mainloop. """
@ -86,7 +86,7 @@ def pytest_load_initial_conftests(args, early_config, parser):
# collection hooks
# -------------------------------------------------------------------------
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_collection(session):
""" perform the collection protocol for the given session. """
@ -97,14 +97,14 @@ def pytest_collection_modifyitems(session, config, items):
def pytest_collection_finish(session):
""" called after collection has been performed and modified. """
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_ignore_collect(path, config):
""" return True to prevent considering this path for collection.
This hook is consulted for all files and directories prior to calling
more specific hooks.
"""
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_collect_directory(path, parent):
""" called before traversing a directory for collection files. """
@ -125,7 +125,7 @@ def pytest_collectreport(report):
def pytest_deselected(items):
""" called for test items deselected by keyword. """
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_make_collect_report(collector):
""" perform ``collector.collect()`` and return a CollectReport. """
@ -133,7 +133,7 @@ def pytest_make_collect_report(collector):
# Python test function related hooks
# -------------------------------------------------------------------------
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_pycollect_makemodule(path, parent):
""" return a Module collector or None for the given path.
This hook will be called for each matching test module path.
@ -141,11 +141,11 @@ def pytest_pycollect_makemodule(path, parent):
create test modules for files that do not match as a test module.
"""
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_pycollect_makeitem(collector, name, obj):
""" return custom item/collector for a python object in a module, or None. """
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_pyfunc_call(pyfuncitem):
""" call underlying test function. """
@ -156,7 +156,7 @@ def pytest_generate_tests(metafunc):
# generic runtest related hooks
# -------------------------------------------------------------------------
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_runtestloop(session):
""" called for performing the main runtest loop
(after collection finished). """
@ -164,7 +164,7 @@ def pytest_runtestloop(session):
def pytest_itemstart(item, node):
""" (deprecated, use pytest_runtest_logstart). """
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_runtest_protocol(item, nextitem):
""" implements the runtest_setup/call/teardown protocol for
the given test item, including capturing exceptions and calling
@ -197,7 +197,7 @@ def pytest_runtest_teardown(item, nextitem):
so that nextitem only needs to call setup-functions.
"""
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_runtest_makereport(item, call):
""" return a :py:class:`_pytest.runner.TestReport` object
for the given :py:class:`pytest.Item` and
@ -242,7 +242,7 @@ def pytest_assertrepr_compare(config, op, left, right):
def pytest_report_header(config, startdir):
""" return a string to be displayed as header info for terminal reporting."""
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_report_teststatus(report):
""" return result-category, shortletter and verbose word for reporting."""
@ -258,7 +258,7 @@ def pytest_logwarning(message, code, nodeid, fslocation):
# doctest hooks
# -------------------------------------------------------------------------
@hookspec_opts(firstresult=True)
@hookspec(firstresult=True)
def pytest_doctest_prepare_content(content):
""" return processed content for a given doctest"""

View File

@ -512,12 +512,12 @@ class Session(FSCollector):
def _makeid(self):
return ""
@pytest.hookimpl_opts(tryfirst=True)
@pytest.hookimpl(tryfirst=True)
def pytest_collectstart(self):
if self.shouldstop:
raise self.Interrupted(self.shouldstop)
@pytest.hookimpl_opts(tryfirst=True)
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_logreport(self, report):
if report.failed and not hasattr(report, 'wasxfail'):
self._testsfailed += 1

View File

@ -24,7 +24,7 @@ def pytest_runtest_makereport(item, call):
call.excinfo = call2.excinfo
@pytest.hookimpl_opts(trylast=True)
@pytest.hookimpl(trylast=True)
def pytest_runtest_setup(item):
if is_potential_nosetest(item):
if isinstance(item.parent, pytest.Generator):

View File

@ -11,7 +11,7 @@ def pytest_addoption(parser):
choices=['failed', 'all'],
help="send failed|all info to bpaste.net pastebin service.")
@pytest.hookimpl_opts(trylast=True)
@pytest.hookimpl(trylast=True)
def pytest_configure(config):
if config.option.pastebin == "all":
tr = config.pluginmanager.getplugin('terminalreporter')

View File

@ -80,7 +80,7 @@ class LsofFdLeakChecker(object):
else:
return True
@pytest.hookimpl_opts(hookwrapper=True, tryfirst=True)
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_item(self, item):
lines1 = self.get_open_files()
yield

View File

@ -181,7 +181,7 @@ def pytest_configure(config):
def pytest_sessionstart(session):
session._fixturemanager = FixtureManager(session)
@pytest.hookimpl_opts(trylast=True)
@pytest.hookimpl(trylast=True)
def pytest_namespace():
raises.Exception = pytest.fail.Exception
return {
@ -200,7 +200,7 @@ def pytestconfig(request):
return request.config
@pytest.hookimpl_opts(trylast=True)
@pytest.hookimpl(trylast=True)
def pytest_pyfunc_call(pyfuncitem):
testfunction = pyfuncitem.obj
if pyfuncitem._isyieldedfunction():
@ -228,7 +228,7 @@ def pytest_collect_file(path, parent):
def pytest_pycollect_makemodule(path, parent):
return Module(path, parent)
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_pycollect_makeitem(collector, name, obj):
outcome = yield
res = outcome.get_result()

View File

@ -133,7 +133,7 @@ class MarkEvaluator:
return expl
@pytest.hookimpl_opts(tryfirst=True)
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_setup(item):
evalskip = MarkEvaluator(item, 'skipif')
if evalskip.istrue():
@ -151,7 +151,7 @@ def check_xfail_no_run(item):
if not evalxfail.get('run', True):
pytest.xfail("[NOTRUN] " + evalxfail.getexplanation())
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
rep = outcome.get_result()

View File

@ -268,7 +268,7 @@ class TerminalReporter:
def pytest_collection_modifyitems(self):
self.report_collect(True)
@pytest.hookimpl_opts(trylast=True)
@pytest.hookimpl(trylast=True)
def pytest_sessionstart(self, session):
self._sessionstarttime = time.time()
if not self.showheader:
@ -355,7 +355,7 @@ class TerminalReporter:
indent = (len(stack) - 1) * " "
self._tw.line("%s%s" % (indent, col))
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_sessionfinish(self, exitstatus):
outcome = yield
outcome.get_result()

View File

@ -140,7 +140,7 @@ class TestCaseFunction(pytest.Function):
if traceback:
excinfo.traceback = traceback
@pytest.hookimpl_opts(tryfirst=True)
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_makereport(item, call):
if isinstance(item, TestCaseFunction):
if item._excinfo:
@ -152,7 +152,7 @@ def pytest_runtest_makereport(item, call):
# twisted trial support
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_protocol(item):
if isinstance(item, TestCaseFunction) and \
'twisted.trial.unittest' in sys.modules:

View File

@ -201,9 +201,9 @@ You can ask which markers exist for your test suite - the list includes our just
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
@pytest.hookimpl_opts(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.hookimpl(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.hookimpl_opts(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
@pytest.hookimpl(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
For an example on how to add and work with markers from a plugin, see
@ -375,9 +375,9 @@ The ``--markers`` option always gives you a list of available markers::
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
@pytest.hookimpl_opts(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.hookimpl(tryfirst=True): mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.hookimpl_opts(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
@pytest.hookimpl(trylast=True): mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
Reading markers which were set from multiple places

View File

@ -534,7 +534,7 @@ case we just write some informations out to a ``failures`` file::
import pytest
import os.path
@pytest.hookimpl_opts(tryfirst=True)
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_makereport(item, call, __multicall__):
# execute all other hooks to obtain the report object
rep = __multicall__.execute()
@ -607,7 +607,7 @@ here is a little example implemented via a local plugin::
import pytest
@pytest.hookimpl_opts(tryfirst=True)
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_makereport(item, call, __multicall__):
# execute all other hooks to obtain the report object
rep = __multicall__.execute()

View File

@ -292,7 +292,7 @@ Here is an example definition of a hook wrapper::
import pytest
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_pyfunc_call(pyfuncitem):
# do whatever you want before the next hook executes
@ -305,8 +305,7 @@ Here is an example definition of a hook wrapper::
Note that hook wrappers don't return results themselves, they merely
perform tracing or other side effects around the actual hook implementations.
If the result of the underlying hook is a mutable object, they may modify
that result, however.
that result but it's probably better to avoid it.
Hook function ordering / call example
@ -338,16 +337,24 @@ after others, i.e. the position in the ``N``-sized list of functions::
Here is the order of execution:
1. Plugin3's pytest_collection_modifyitems called until the yield point
2. Plugin1's pytest_collection_modifyitems is called
3. Plugin2's pytest_collection_modifyitems is called
4. Plugin3's pytest_collection_modifyitems called for executing after the yield
The yield receives a :py:class:`CallOutcome` instance which encapsulates
the result from calling the non-wrappers. Wrappers cannot modify the result.
because it is a hook wrapper.
2. Plugin1's pytest_collection_modifyitems is called because it is marked
with ``tryfirst=True``.
3. Plugin2's pytest_collection_modifyitems is called because it is marked
with ``trylast=True`` (but even without this mark it would come after
Plugin1).
4. Plugin3's pytest_collection_modifyitems then executing the code after the yield
point. The yield receives a :py:class:`CallOutcome` instance which encapsulates
the result from calling the non-wrappers. Wrappers shall not modify the result.
It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with
``hookwrapper=True`` in which case it will influence the ordering of hookwrappers
among each other.
Declaring new hooks
------------------------
@ -368,11 +375,11 @@ For an example, see `newhooks.py`_ from :ref:`xdist`.
.. _`newhooks.py`: https://bitbucket.org/pytest-dev/pytest-xdist/src/52082f70e7dd04b00361091b8af906c60fd6700f/xdist/newhooks.py?at=default
Using hooks from 3rd party plugins
-------------------------------------
Optionally using hooks from 3rd party plugins
---------------------------------------------
Using new hooks from plugins as explained above might be a little tricky
because the standard :ref:`validation mechanism <validation>`:
because of the standard :ref:`validation mechanism <validation>`:
if you depend on a plugin that is not installed, validation will fail and
the error message will not make much sense to your users.
@ -395,7 +402,6 @@ declaring the hook functions directly in your plugin module, for example::
This has the added benefit of allowing you to conditionally install hooks
depending on which plugins are installed.
.. _`well specified hooks`:
.. currentmodule:: _pytest.hookspec

View File

@ -13,7 +13,7 @@ if __name__ == '__main__': # if run as a script or by 'python -m pytest'
from _pytest.config import (
main, UsageError, _preloadplugins, cmdline,
hookspec_opts, hookimpl_opts
hookspec, hookimpl
)
from _pytest import __version__

View File

@ -559,7 +559,7 @@ class TestConftestCustomization:
b = testdir.mkdir("a").mkdir("b")
b.join("conftest.py").write(py.code.Source("""
import pytest
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_pycollect_makeitem():
outcome = yield
if outcome.excinfo is None:

View File

@ -38,7 +38,7 @@ def test_hookvalidation_unknown(testdir):
def test_hookvalidation_optional(testdir):
testdir.makeconftest("""
import pytest
@pytest.hookimpl_opts(optionalhook=True)
@pytest.hookimpl(optionalhook=True)
def pytest_hello(xyz):
pass
""")

View File

@ -510,7 +510,7 @@ class TestKeywordSelection:
""")
testdir.makepyfile(conftest="""
import pytest
@pytest.hookimpl_opts(hookwrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_pycollect_makeitem(name):
outcome = yield
if name == "TestClass":