add documented hookimpl_opts and hookspec_opts decorators
so that one doesn't have to use pytest.mark or function-attribute setting anymore --HG-- branch : more_plugin
This commit is contained in:
parent
bbbb6dc2e3
commit
d2a5c7f99b
|
@ -26,6 +26,13 @@
|
||||||
change but it might still break 3rd party plugins which relied on
|
change but it might still break 3rd party plugins which relied on
|
||||||
details like especially the pluginmanager.add_shutdown() API.
|
details like especially the pluginmanager.add_shutdown() API.
|
||||||
Thanks Holger Krekel.
|
Thanks Holger Krekel.
|
||||||
|
|
||||||
|
- pluginmanagement: introduce ``pytest.hookimpl_opts`` and
|
||||||
|
``pytest.hookspec_opts`` 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.
|
||||||
|
|
||||||
|
|
||||||
2.7.1.dev (compared to 2.7.0)
|
2.7.1.dev (compared to 2.7.0)
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
|
@ -29,7 +29,7 @@ def pytest_addoption(parser):
|
||||||
help="shortcut for --capture=no.")
|
help="shortcut for --capture=no.")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_load_initial_conftests(early_config, parser, args):
|
def pytest_load_initial_conftests(early_config, parser, args):
|
||||||
ns = early_config.known_args_namespace
|
ns = early_config.known_args_namespace
|
||||||
pluginmanager = early_config.pluginmanager
|
pluginmanager = early_config.pluginmanager
|
||||||
|
@ -101,7 +101,7 @@ class CaptureManager:
|
||||||
if capfuncarg is not None:
|
if capfuncarg is not None:
|
||||||
capfuncarg.close()
|
capfuncarg.close()
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_make_collect_report(self, collector):
|
def pytest_make_collect_report(self, collector):
|
||||||
if isinstance(collector, pytest.File):
|
if isinstance(collector, pytest.File):
|
||||||
self.resumecapture()
|
self.resumecapture()
|
||||||
|
@ -115,13 +115,13 @@ class CaptureManager:
|
||||||
else:
|
else:
|
||||||
yield
|
yield
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_runtest_setup(self, item):
|
def pytest_runtest_setup(self, item):
|
||||||
self.resumecapture()
|
self.resumecapture()
|
||||||
yield
|
yield
|
||||||
self.suspendcapture_item(item, "setup")
|
self.suspendcapture_item(item, "setup")
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_runtest_call(self, item):
|
def pytest_runtest_call(self, item):
|
||||||
self.resumecapture()
|
self.resumecapture()
|
||||||
self.activate_funcargs(item)
|
self.activate_funcargs(item)
|
||||||
|
@ -129,17 +129,17 @@ class CaptureManager:
|
||||||
#self.deactivate_funcargs() called from suspendcapture()
|
#self.deactivate_funcargs() called from suspendcapture()
|
||||||
self.suspendcapture_item(item, "call")
|
self.suspendcapture_item(item, "call")
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_runtest_teardown(self, item):
|
def pytest_runtest_teardown(self, item):
|
||||||
self.resumecapture()
|
self.resumecapture()
|
||||||
yield
|
yield
|
||||||
self.suspendcapture_item(item, "teardown")
|
self.suspendcapture_item(item, "teardown")
|
||||||
|
|
||||||
@pytest.mark.tryfirst
|
@pytest.hookimpl_opts(tryfirst=True)
|
||||||
def pytest_keyboard_interrupt(self, excinfo):
|
def pytest_keyboard_interrupt(self, excinfo):
|
||||||
self.reset_capturings()
|
self.reset_capturings()
|
||||||
|
|
||||||
@pytest.mark.tryfirst
|
@pytest.hookimpl_opts(tryfirst=True)
|
||||||
def pytest_internalerror(self, excinfo):
|
def pytest_internalerror(self, excinfo):
|
||||||
self.reset_capturings()
|
self.reset_capturings()
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,52 @@ import py
|
||||||
|
|
||||||
py3 = sys.version_info > (3,0)
|
py3 = sys.version_info > (3,0)
|
||||||
|
|
||||||
|
def hookspec_opts(firstresult=False):
|
||||||
|
""" returns a decorator which will define a function as a hook specfication.
|
||||||
|
|
||||||
|
If firstresult is True the 1:N hook call (N being the number of registered
|
||||||
|
hook implementation functions) will stop at I<=N when the I'th function
|
||||||
|
returns a non-None result.
|
||||||
|
"""
|
||||||
|
def setattr_hookspec_opts(func):
|
||||||
|
if firstresult:
|
||||||
|
func.firstresult = firstresult
|
||||||
|
return func
|
||||||
|
return setattr_hookspec_opts
|
||||||
|
|
||||||
|
|
||||||
|
def hookimpl_opts(hookwrapper=False, optionalhook=False,
|
||||||
|
tryfirst=False, trylast=False):
|
||||||
|
""" Return a decorator which marks a function as a hook implementation.
|
||||||
|
|
||||||
|
If optionalhook is True a missing matching hook specification will not result
|
||||||
|
in an error (by default it is an error if no matching spec is found).
|
||||||
|
|
||||||
|
If tryfirst is True this hook implementation will run as early as possible
|
||||||
|
in the chain of N hook implementations for a specfication.
|
||||||
|
|
||||||
|
If trylast is True this hook implementation will run as late as possible
|
||||||
|
in the chain of N hook implementations.
|
||||||
|
|
||||||
|
If hookwrapper is True the hook implementations needs to execute exactly
|
||||||
|
one "yield". The code before the yield is run early before any non-hookwrapper
|
||||||
|
function is run. The code after the yield is run after all non-hookwrapper
|
||||||
|
function have run. The yield receives an ``CallOutcome`` object representing
|
||||||
|
the exception or result outcome of the inner calls (including other hookwrapper
|
||||||
|
calls).
|
||||||
|
"""
|
||||||
|
def setattr_hookimpl_opts(func):
|
||||||
|
if hookwrapper:
|
||||||
|
func.hookwrapper = True
|
||||||
|
if optionalhook:
|
||||||
|
func.optionalhook = True
|
||||||
|
if tryfirst:
|
||||||
|
func.tryfirst = True
|
||||||
|
if trylast:
|
||||||
|
func.trylast = True
|
||||||
|
return func
|
||||||
|
return setattr_hookimpl_opts
|
||||||
|
|
||||||
class TagTracer:
|
class TagTracer:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._tag2proc = {}
|
self._tag2proc = {}
|
||||||
|
|
|
@ -22,7 +22,7 @@ def pytest_addoption(parser):
|
||||||
help="store internal tracing debug information in 'pytestdebug.log'.")
|
help="store internal tracing debug information in 'pytestdebug.log'.")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_cmdline_parse():
|
def pytest_cmdline_parse():
|
||||||
outcome = yield
|
outcome = yield
|
||||||
config = outcome.get_result()
|
config = outcome.get_result()
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
|
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
|
||||||
|
|
||||||
|
from _pytest.core import hookspec_opts
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
# Initialization
|
# Initialization
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
@ -15,9 +17,9 @@ def pytest_namespace():
|
||||||
are parsed.
|
are parsed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_cmdline_parse(pluginmanager, args):
|
def pytest_cmdline_parse(pluginmanager, args):
|
||||||
"""return initialized config object, parsing the specified args. """
|
"""return initialized config object, parsing the specified args. """
|
||||||
pytest_cmdline_parse.firstresult = True
|
|
||||||
|
|
||||||
def pytest_cmdline_preparse(config, args):
|
def pytest_cmdline_preparse(config, args):
|
||||||
"""(deprecated) modify command line arguments before option parsing. """
|
"""(deprecated) modify command line arguments before option parsing. """
|
||||||
|
@ -47,10 +49,10 @@ def pytest_addoption(parser):
|
||||||
via (deprecated) ``pytest.config``.
|
via (deprecated) ``pytest.config``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_cmdline_main(config):
|
def pytest_cmdline_main(config):
|
||||||
""" called for performing the main command line action. The default
|
""" called for performing the main command line action. The default
|
||||||
implementation will invoke the configure hooks and runtest_mainloop. """
|
implementation will invoke the configure hooks and runtest_mainloop. """
|
||||||
pytest_cmdline_main.firstresult = True
|
|
||||||
|
|
||||||
def pytest_load_initial_conftests(args, early_config, parser):
|
def pytest_load_initial_conftests(args, early_config, parser):
|
||||||
""" implements the loading of initial conftest files ahead
|
""" implements the loading of initial conftest files ahead
|
||||||
|
@ -64,18 +66,18 @@ def pytest_configure(config):
|
||||||
def pytest_unconfigure(config):
|
def pytest_unconfigure(config):
|
||||||
""" called before test process is exited. """
|
""" called before test process is exited. """
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_runtestloop(session):
|
def pytest_runtestloop(session):
|
||||||
""" called for performing the main runtest loop
|
""" called for performing the main runtest loop
|
||||||
(after collection finished). """
|
(after collection finished). """
|
||||||
pytest_runtestloop.firstresult = True
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
# collection hooks
|
# collection hooks
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_collection(session):
|
def pytest_collection(session):
|
||||||
""" perform the collection protocol for the given session. """
|
""" perform the collection protocol for the given session. """
|
||||||
pytest_collection.firstresult = True
|
|
||||||
|
|
||||||
def pytest_collection_modifyitems(session, config, items):
|
def pytest_collection_modifyitems(session, config, items):
|
||||||
""" called after collection has been performed, may filter or re-order
|
""" called after collection has been performed, may filter or re-order
|
||||||
|
@ -84,16 +86,16 @@ def pytest_collection_modifyitems(session, config, items):
|
||||||
def pytest_collection_finish(session):
|
def pytest_collection_finish(session):
|
||||||
""" called after collection has been performed and modified. """
|
""" called after collection has been performed and modified. """
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_ignore_collect(path, config):
|
def pytest_ignore_collect(path, config):
|
||||||
""" return True to prevent considering this path for collection.
|
""" return True to prevent considering this path for collection.
|
||||||
This hook is consulted for all files and directories prior to calling
|
This hook is consulted for all files and directories prior to calling
|
||||||
more specific hooks.
|
more specific hooks.
|
||||||
"""
|
"""
|
||||||
pytest_ignore_collect.firstresult = True
|
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_collect_directory(path, parent):
|
def pytest_collect_directory(path, parent):
|
||||||
""" called before traversing a directory for collection files. """
|
""" called before traversing a directory for collection files. """
|
||||||
pytest_collect_directory.firstresult = True
|
|
||||||
|
|
||||||
def pytest_collect_file(path, parent):
|
def pytest_collect_file(path, parent):
|
||||||
""" return collection Node or None for the given path. Any new node
|
""" return collection Node or None for the given path. Any new node
|
||||||
|
@ -112,29 +114,29 @@ def pytest_collectreport(report):
|
||||||
def pytest_deselected(items):
|
def pytest_deselected(items):
|
||||||
""" called for test items deselected by keyword. """
|
""" called for test items deselected by keyword. """
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_make_collect_report(collector):
|
def pytest_make_collect_report(collector):
|
||||||
""" perform ``collector.collect()`` and return a CollectReport. """
|
""" perform ``collector.collect()`` and return a CollectReport. """
|
||||||
pytest_make_collect_report.firstresult = True
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
# Python test function related hooks
|
# Python test function related hooks
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_pycollect_makemodule(path, parent):
|
def pytest_pycollect_makemodule(path, parent):
|
||||||
""" return a Module collector or None for the given path.
|
""" return a Module collector or None for the given path.
|
||||||
This hook will be called for each matching test module path.
|
This hook will be called for each matching test module path.
|
||||||
The pytest_collect_file hook needs to be used if you want to
|
The pytest_collect_file hook needs to be used if you want to
|
||||||
create test modules for files that do not match as a test module.
|
create test modules for files that do not match as a test module.
|
||||||
"""
|
"""
|
||||||
pytest_pycollect_makemodule.firstresult = True
|
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_pycollect_makeitem(collector, name, obj):
|
def pytest_pycollect_makeitem(collector, name, obj):
|
||||||
""" return custom item/collector for a python object in a module, or None. """
|
""" return custom item/collector for a python object in a module, or None. """
|
||||||
pytest_pycollect_makeitem.firstresult = True
|
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_pyfunc_call(pyfuncitem):
|
def pytest_pyfunc_call(pyfuncitem):
|
||||||
""" call underlying test function. """
|
""" call underlying test function. """
|
||||||
pytest_pyfunc_call.firstresult = True
|
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
""" generate (multiple) parametrized calls to a test function."""
|
""" generate (multiple) parametrized calls to a test function."""
|
||||||
|
@ -145,6 +147,7 @@ def pytest_generate_tests(metafunc):
|
||||||
def pytest_itemstart(item, node):
|
def pytest_itemstart(item, node):
|
||||||
""" (deprecated, use pytest_runtest_logstart). """
|
""" (deprecated, use pytest_runtest_logstart). """
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_runtest_protocol(item, nextitem):
|
def pytest_runtest_protocol(item, nextitem):
|
||||||
""" implements the runtest_setup/call/teardown protocol for
|
""" implements the runtest_setup/call/teardown protocol for
|
||||||
the given test item, including capturing exceptions and calling
|
the given test item, including capturing exceptions and calling
|
||||||
|
@ -158,7 +161,6 @@ def pytest_runtest_protocol(item, nextitem):
|
||||||
|
|
||||||
:return boolean: True if no further hook implementations should be invoked.
|
:return boolean: True if no further hook implementations should be invoked.
|
||||||
"""
|
"""
|
||||||
pytest_runtest_protocol.firstresult = True
|
|
||||||
|
|
||||||
def pytest_runtest_logstart(nodeid, location):
|
def pytest_runtest_logstart(nodeid, location):
|
||||||
""" signal the start of running a single test item. """
|
""" signal the start of running a single test item. """
|
||||||
|
@ -178,12 +180,12 @@ def pytest_runtest_teardown(item, nextitem):
|
||||||
so that nextitem only needs to call setup-functions.
|
so that nextitem only needs to call setup-functions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
""" return a :py:class:`_pytest.runner.TestReport` object
|
""" return a :py:class:`_pytest.runner.TestReport` object
|
||||||
for the given :py:class:`pytest.Item` and
|
for the given :py:class:`pytest.Item` and
|
||||||
:py:class:`_pytest.runner.CallInfo`.
|
:py:class:`_pytest.runner.CallInfo`.
|
||||||
"""
|
"""
|
||||||
pytest_runtest_makereport.firstresult = True
|
|
||||||
|
|
||||||
def pytest_runtest_logreport(report):
|
def pytest_runtest_logreport(report):
|
||||||
""" process a test setup/call/teardown report relating to
|
""" process a test setup/call/teardown report relating to
|
||||||
|
@ -220,9 +222,9 @@ def pytest_assertrepr_compare(config, op, left, right):
|
||||||
def pytest_report_header(config, startdir):
|
def pytest_report_header(config, startdir):
|
||||||
""" return a string to be displayed as header info for terminal reporting."""
|
""" return a string to be displayed as header info for terminal reporting."""
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_report_teststatus(report):
|
def pytest_report_teststatus(report):
|
||||||
""" return result-category, shortletter and verbose word for reporting."""
|
""" return result-category, shortletter and verbose word for reporting."""
|
||||||
pytest_report_teststatus.firstresult = True
|
|
||||||
|
|
||||||
def pytest_terminal_summary(terminalreporter):
|
def pytest_terminal_summary(terminalreporter):
|
||||||
""" add additional section in terminal summary reporting. """
|
""" add additional section in terminal summary reporting. """
|
||||||
|
@ -236,9 +238,9 @@ def pytest_logwarning(message, code, nodeid, fslocation):
|
||||||
# doctest hooks
|
# doctest hooks
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
def pytest_doctest_prepare_content(content):
|
def pytest_doctest_prepare_content(content):
|
||||||
""" return processed content for a given doctest"""
|
""" return processed content for a given doctest"""
|
||||||
pytest_doctest_prepare_content.firstresult = True
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
# error handling and internal debugging hooks
|
# error handling and internal debugging hooks
|
||||||
|
|
|
@ -519,12 +519,12 @@ class Session(FSCollector):
|
||||||
def _makeid(self):
|
def _makeid(self):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@pytest.mark.tryfirst
|
@pytest.hookimpl_opts(tryfirst=True)
|
||||||
def pytest_collectstart(self):
|
def pytest_collectstart(self):
|
||||||
if self.shouldstop:
|
if self.shouldstop:
|
||||||
raise self.Interrupted(self.shouldstop)
|
raise self.Interrupted(self.shouldstop)
|
||||||
|
|
||||||
@pytest.mark.tryfirst
|
@pytest.hookimpl_opts(tryfirst=True)
|
||||||
def pytest_runtest_logreport(self, report):
|
def pytest_runtest_logreport(self, report):
|
||||||
if report.failed and not hasattr(report, 'wasxfail'):
|
if report.failed and not hasattr(report, 'wasxfail'):
|
||||||
self._testsfailed += 1
|
self._testsfailed += 1
|
||||||
|
|
|
@ -24,7 +24,7 @@ def pytest_runtest_makereport(item, call):
|
||||||
call.excinfo = call2.excinfo
|
call.excinfo = call2.excinfo
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.trylast
|
@pytest.hookimpl_opts(trylast=True)
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
if is_potential_nosetest(item):
|
if is_potential_nosetest(item):
|
||||||
if isinstance(item.parent, pytest.Generator):
|
if isinstance(item.parent, pytest.Generator):
|
||||||
|
|
|
@ -11,7 +11,7 @@ def pytest_addoption(parser):
|
||||||
choices=['failed', 'all'],
|
choices=['failed', 'all'],
|
||||||
help="send failed|all info to bpaste.net pastebin service.")
|
help="send failed|all info to bpaste.net pastebin service.")
|
||||||
|
|
||||||
@pytest.mark.trylast
|
@pytest.hookimpl_opts(trylast=True)
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
if config.option.pastebin == "all":
|
if config.option.pastebin == "all":
|
||||||
tr = config.pluginmanager.getplugin('terminalreporter')
|
tr = config.pluginmanager.getplugin('terminalreporter')
|
||||||
|
|
|
@ -172,7 +172,7 @@ def pytest_configure(config):
|
||||||
def pytest_sessionstart(session):
|
def pytest_sessionstart(session):
|
||||||
session._fixturemanager = FixtureManager(session)
|
session._fixturemanager = FixtureManager(session)
|
||||||
|
|
||||||
@pytest.mark.trylast
|
@pytest.hookimpl_opts(trylast=True)
|
||||||
def pytest_namespace():
|
def pytest_namespace():
|
||||||
raises.Exception = pytest.fail.Exception
|
raises.Exception = pytest.fail.Exception
|
||||||
return {
|
return {
|
||||||
|
@ -191,7 +191,7 @@ def pytestconfig(request):
|
||||||
return request.config
|
return request.config
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.trylast
|
@pytest.hookimpl_opts(trylast=True)
|
||||||
def pytest_pyfunc_call(pyfuncitem):
|
def pytest_pyfunc_call(pyfuncitem):
|
||||||
testfunction = pyfuncitem.obj
|
testfunction = pyfuncitem.obj
|
||||||
if pyfuncitem._isyieldedfunction():
|
if pyfuncitem._isyieldedfunction():
|
||||||
|
@ -219,7 +219,7 @@ def pytest_collect_file(path, parent):
|
||||||
def pytest_pycollect_makemodule(path, parent):
|
def pytest_pycollect_makemodule(path, parent):
|
||||||
return Module(path, parent)
|
return Module(path, parent)
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_pycollect_makeitem(collector, name, obj):
|
def pytest_pycollect_makeitem(collector, name, obj):
|
||||||
outcome = yield
|
outcome = yield
|
||||||
res = outcome.get_result()
|
res = outcome.get_result()
|
||||||
|
@ -1667,7 +1667,7 @@ class FixtureManager:
|
||||||
self.parsefactories(plugin, nodeid)
|
self.parsefactories(plugin, nodeid)
|
||||||
self._seenplugins.add(plugin)
|
self._seenplugins.add(plugin)
|
||||||
|
|
||||||
@pytest.mark.tryfirst
|
@pytest.hookimpl_opts(tryfirst=True)
|
||||||
def pytest_configure(self, config):
|
def pytest_configure(self, config):
|
||||||
plugins = config.pluginmanager.getplugins()
|
plugins = config.pluginmanager.getplugins()
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
|
|
|
@ -133,7 +133,7 @@ class MarkEvaluator:
|
||||||
return expl
|
return expl
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.tryfirst
|
@pytest.hookimpl_opts(tryfirst=True)
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
evalskip = MarkEvaluator(item, 'skipif')
|
evalskip = MarkEvaluator(item, 'skipif')
|
||||||
if evalskip.istrue():
|
if evalskip.istrue():
|
||||||
|
@ -151,7 +151,7 @@ def check_xfail_no_run(item):
|
||||||
if not evalxfail.get('run', True):
|
if not evalxfail.get('run', True):
|
||||||
pytest.xfail("[NOTRUN] " + evalxfail.getexplanation())
|
pytest.xfail("[NOTRUN] " + evalxfail.getexplanation())
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
outcome = yield
|
outcome = yield
|
||||||
rep = outcome.get_result()
|
rep = outcome.get_result()
|
||||||
|
|
|
@ -265,7 +265,7 @@ class TerminalReporter:
|
||||||
def pytest_collection_modifyitems(self):
|
def pytest_collection_modifyitems(self):
|
||||||
self.report_collect(True)
|
self.report_collect(True)
|
||||||
|
|
||||||
@pytest.mark.trylast
|
@pytest.hookimpl_opts(trylast=True)
|
||||||
def pytest_sessionstart(self, session):
|
def pytest_sessionstart(self, session):
|
||||||
self._sessionstarttime = time.time()
|
self._sessionstarttime = time.time()
|
||||||
if not self.showheader:
|
if not self.showheader:
|
||||||
|
@ -350,7 +350,7 @@ class TerminalReporter:
|
||||||
indent = (len(stack) - 1) * " "
|
indent = (len(stack) - 1) * " "
|
||||||
self._tw.line("%s%s" % (indent, col))
|
self._tw.line("%s%s" % (indent, col))
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_sessionfinish(self, exitstatus):
|
def pytest_sessionfinish(self, exitstatus):
|
||||||
outcome = yield
|
outcome = yield
|
||||||
outcome.get_result()
|
outcome.get_result()
|
||||||
|
|
|
@ -140,7 +140,7 @@ class TestCaseFunction(pytest.Function):
|
||||||
if traceback:
|
if traceback:
|
||||||
excinfo.traceback = traceback
|
excinfo.traceback = traceback
|
||||||
|
|
||||||
@pytest.mark.tryfirst
|
@pytest.hookimpl_opts(tryfirst=True)
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
if isinstance(item, TestCaseFunction):
|
if isinstance(item, TestCaseFunction):
|
||||||
if item._excinfo:
|
if item._excinfo:
|
||||||
|
@ -152,7 +152,7 @@ def pytest_runtest_makereport(item, call):
|
||||||
|
|
||||||
# twisted trial support
|
# twisted trial support
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_runtest_protocol(item):
|
def pytest_runtest_protocol(item):
|
||||||
if isinstance(item, TestCaseFunction) and \
|
if isinstance(item, TestCaseFunction) and \
|
||||||
'twisted.trial.unittest' in sys.modules:
|
'twisted.trial.unittest' in sys.modules:
|
||||||
|
|
|
@ -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.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
|
||||||
|
|
||||||
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
|
@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.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late 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.
|
||||||
|
|
||||||
|
|
||||||
For an example on how to add and work with markers from a plugin, see
|
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.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
|
||||||
|
|
||||||
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
|
@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.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late 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.
|
||||||
|
|
||||||
|
|
||||||
Reading markers which were set from multiple places
|
Reading markers which were set from multiple places
|
||||||
|
|
|
@ -534,7 +534,7 @@ case we just write some informations out to a ``failures`` file::
|
||||||
import pytest
|
import pytest
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
@pytest.mark.tryfirst
|
@pytest.hookimpl_opts(tryfirst=True)
|
||||||
def pytest_runtest_makereport(item, call, __multicall__):
|
def pytest_runtest_makereport(item, call, __multicall__):
|
||||||
# execute all other hooks to obtain the report object
|
# execute all other hooks to obtain the report object
|
||||||
rep = __multicall__.execute()
|
rep = __multicall__.execute()
|
||||||
|
@ -607,7 +607,7 @@ here is a little example implemented via a local plugin::
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.mark.tryfirst
|
@pytest.hookimpl_opts(tryfirst=True)
|
||||||
def pytest_runtest_makereport(item, call, __multicall__):
|
def pytest_runtest_makereport(item, call, __multicall__):
|
||||||
# execute all other hooks to obtain the report object
|
# execute all other hooks to obtain the report object
|
||||||
rep = __multicall__.execute()
|
rep = __multicall__.execute()
|
||||||
|
|
|
@ -458,7 +458,7 @@ Here is an example definition of a hook wrapper::
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_pyfunc_call(pyfuncitem):
|
def pytest_pyfunc_call(pyfuncitem):
|
||||||
# do whatever you want before the next hook executes
|
# do whatever you want before the next hook executes
|
||||||
outcome = yield
|
outcome = yield
|
||||||
|
|
|
@ -12,6 +12,7 @@ if __name__ == '__main__': # if run as a script or by 'python -m pytest'
|
||||||
# else we are imported
|
# else we are imported
|
||||||
|
|
||||||
from _pytest.config import main, UsageError, _preloadplugins, cmdline
|
from _pytest.config import main, UsageError, _preloadplugins, cmdline
|
||||||
|
from _pytest.core import hookspec_opts, hookimpl_opts
|
||||||
from _pytest import __version__
|
from _pytest import __version__
|
||||||
|
|
||||||
_preloadplugins() # to populate pytest.* namespace so help(pytest) works
|
_preloadplugins() # to populate pytest.* namespace so help(pytest) works
|
||||||
|
|
|
@ -66,7 +66,7 @@ def check_open_files(config):
|
||||||
error.append(error[0])
|
error.append(error[0])
|
||||||
raise AssertionError("\n".join(error))
|
raise AssertionError("\n".join(error))
|
||||||
|
|
||||||
@pytest.mark.trylast
|
@pytest.hookimpl_opts(trylast=True)
|
||||||
def pytest_runtest_teardown(item, __multicall__):
|
def pytest_runtest_teardown(item, __multicall__):
|
||||||
item.config._basedir.chdir()
|
item.config._basedir.chdir()
|
||||||
if hasattr(item.config, '_openfiles'):
|
if hasattr(item.config, '_openfiles'):
|
||||||
|
|
|
@ -563,7 +563,7 @@ class TestConftestCustomization:
|
||||||
b = testdir.mkdir("a").mkdir("b")
|
b = testdir.mkdir("a").mkdir("b")
|
||||||
b.join("conftest.py").write(py.code.Source("""
|
b.join("conftest.py").write(py.code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_pycollect_makeitem():
|
def pytest_pycollect_makeitem():
|
||||||
outcome = yield
|
outcome = yield
|
||||||
if outcome.excinfo is None:
|
if outcome.excinfo is None:
|
||||||
|
|
|
@ -192,6 +192,53 @@ class TestAddMethodOrdering:
|
||||||
assert hc.nonwrappers == [he_method1_middle]
|
assert hc.nonwrappers == [he_method1_middle]
|
||||||
assert hc.wrappers == [he_method1, he_method3]
|
assert hc.wrappers == [he_method1, he_method3]
|
||||||
|
|
||||||
|
def test_hookspec_opts(self, pm):
|
||||||
|
class HookSpec:
|
||||||
|
@hookspec_opts()
|
||||||
|
def he_myhook1(self, arg1):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
|
def he_myhook2(self, arg1):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@hookspec_opts(firstresult=False)
|
||||||
|
def he_myhook3(self, arg1):
|
||||||
|
pass
|
||||||
|
|
||||||
|
pm.addhooks(HookSpec)
|
||||||
|
assert not pm.hook.he_myhook1.firstresult
|
||||||
|
assert pm.hook.he_myhook2.firstresult
|
||||||
|
assert not pm.hook.he_myhook3.firstresult
|
||||||
|
|
||||||
|
|
||||||
|
def test_hookimpl_opts(self):
|
||||||
|
for name in ["hookwrapper", "optionalhook", "tryfirst", "trylast"]:
|
||||||
|
for val in [True, False]:
|
||||||
|
@hookimpl_opts(**{name: val})
|
||||||
|
def he_myhook1(self, arg1):
|
||||||
|
pass
|
||||||
|
if val:
|
||||||
|
assert getattr(he_myhook1, name)
|
||||||
|
else:
|
||||||
|
assert not hasattr(he_myhook1, name)
|
||||||
|
|
||||||
|
def test_decorator_functional(self, pm):
|
||||||
|
class HookSpec:
|
||||||
|
@hookspec_opts(firstresult=True)
|
||||||
|
def he_myhook(self, arg1):
|
||||||
|
""" add to arg1 """
|
||||||
|
pm.addhooks(HookSpec)
|
||||||
|
|
||||||
|
class Plugin:
|
||||||
|
@hookimpl_opts()
|
||||||
|
def he_myhook(self, arg1):
|
||||||
|
return arg1 + 1
|
||||||
|
|
||||||
|
pm.register(Plugin())
|
||||||
|
results = pm.hook.he_myhook(arg1=17)
|
||||||
|
assert results == 18
|
||||||
|
|
||||||
|
|
||||||
class TestPytestPluginInteractions:
|
class TestPytestPluginInteractions:
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ def test_hookvalidation_unknown(testdir):
|
||||||
def test_hookvalidation_optional(testdir):
|
def test_hookvalidation_optional(testdir):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.mark.optionalhook
|
@pytest.hookimpl_opts(optionalhook=True)
|
||||||
def pytest_hello(xyz):
|
def pytest_hello(xyz):
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
|
|
|
@ -510,7 +510,7 @@ class TestKeywordSelection:
|
||||||
""")
|
""")
|
||||||
testdir.makepyfile(conftest="""
|
testdir.makepyfile(conftest="""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.mark.hookwrapper
|
@pytest.hookimpl_opts(hookwrapper=True)
|
||||||
def pytest_pycollect_makeitem(name):
|
def pytest_pycollect_makeitem(name):
|
||||||
outcome = yield
|
outcome = yield
|
||||||
if name == "TestClass":
|
if name == "TestClass":
|
||||||
|
|
Loading…
Reference in New Issue