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
|
||||
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
|
||||
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)
|
||||
-----------------------------
|
||||
|
|
|
@ -29,7 +29,7 @@ def pytest_addoption(parser):
|
|||
help="shortcut for --capture=no.")
|
||||
|
||||
|
||||
@pytest.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(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.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(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.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(hookwrapper=True)
|
||||
def pytest_runtest_setup(self, item):
|
||||
self.resumecapture()
|
||||
yield
|
||||
self.suspendcapture_item(item, "setup")
|
||||
|
||||
@pytest.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(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.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(hookwrapper=True)
|
||||
def pytest_runtest_teardown(self, item):
|
||||
self.resumecapture()
|
||||
yield
|
||||
self.suspendcapture_item(item, "teardown")
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.hookimpl_opts(tryfirst=True)
|
||||
def pytest_keyboard_interrupt(self, excinfo):
|
||||
self.reset_capturings()
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.hookimpl_opts(tryfirst=True)
|
||||
def pytest_internalerror(self, excinfo):
|
||||
self.reset_capturings()
|
||||
|
||||
|
|
|
@ -7,6 +7,52 @@ import py
|
|||
|
||||
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:
|
||||
def __init__(self):
|
||||
self._tag2proc = {}
|
||||
|
|
|
@ -22,7 +22,7 @@ def pytest_addoption(parser):
|
|||
help="store internal tracing debug information in 'pytestdebug.log'.")
|
||||
|
||||
|
||||
@pytest.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(hookwrapper=True)
|
||||
def pytest_cmdline_parse():
|
||||
outcome = yield
|
||||
config = outcome.get_result()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
|
||||
|
||||
from _pytest.core import hookspec_opts
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Initialization
|
||||
# -------------------------------------------------------------------------
|
||||
|
@ -15,9 +17,9 @@ def pytest_namespace():
|
|||
are parsed.
|
||||
"""
|
||||
|
||||
@hookspec_opts(firstresult=True)
|
||||
def pytest_cmdline_parse(pluginmanager, args):
|
||||
"""return initialized config object, parsing the specified args. """
|
||||
pytest_cmdline_parse.firstresult = True
|
||||
|
||||
def pytest_cmdline_preparse(config, args):
|
||||
"""(deprecated) modify command line arguments before option parsing. """
|
||||
|
@ -47,10 +49,10 @@ def pytest_addoption(parser):
|
|||
via (deprecated) ``pytest.config``.
|
||||
"""
|
||||
|
||||
@hookspec_opts(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. """
|
||||
pytest_cmdline_main.firstresult = True
|
||||
|
||||
def pytest_load_initial_conftests(args, early_config, parser):
|
||||
""" implements the loading of initial conftest files ahead
|
||||
|
@ -64,18 +66,18 @@ def pytest_configure(config):
|
|||
def pytest_unconfigure(config):
|
||||
""" called before test process is exited. """
|
||||
|
||||
@hookspec_opts(firstresult=True)
|
||||
def pytest_runtestloop(session):
|
||||
""" called for performing the main runtest loop
|
||||
(after collection finished). """
|
||||
pytest_runtestloop.firstresult = True
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# collection hooks
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@hookspec_opts(firstresult=True)
|
||||
def pytest_collection(session):
|
||||
""" perform the collection protocol for the given session. """
|
||||
pytest_collection.firstresult = True
|
||||
|
||||
def pytest_collection_modifyitems(session, config, items):
|
||||
""" 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):
|
||||
""" called after collection has been performed and modified. """
|
||||
|
||||
@hookspec_opts(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.
|
||||
"""
|
||||
pytest_ignore_collect.firstresult = True
|
||||
|
||||
@hookspec_opts(firstresult=True)
|
||||
def pytest_collect_directory(path, parent):
|
||||
""" called before traversing a directory for collection files. """
|
||||
pytest_collect_directory.firstresult = True
|
||||
|
||||
def pytest_collect_file(path, parent):
|
||||
""" return collection Node or None for the given path. Any new node
|
||||
|
@ -112,29 +114,29 @@ def pytest_collectreport(report):
|
|||
def pytest_deselected(items):
|
||||
""" called for test items deselected by keyword. """
|
||||
|
||||
@hookspec_opts(firstresult=True)
|
||||
def pytest_make_collect_report(collector):
|
||||
""" perform ``collector.collect()`` and return a CollectReport. """
|
||||
pytest_make_collect_report.firstresult = True
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Python test function related hooks
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@hookspec_opts(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.
|
||||
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.
|
||||
"""
|
||||
pytest_pycollect_makemodule.firstresult = True
|
||||
|
||||
@hookspec_opts(firstresult=True)
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
""" 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):
|
||||
""" call underlying test function. """
|
||||
pytest_pyfunc_call.firstresult = True
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
""" generate (multiple) parametrized calls to a test function."""
|
||||
|
@ -145,6 +147,7 @@ def pytest_generate_tests(metafunc):
|
|||
def pytest_itemstart(item, node):
|
||||
""" (deprecated, use pytest_runtest_logstart). """
|
||||
|
||||
@hookspec_opts(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
|
||||
|
@ -158,7 +161,6 @@ def pytest_runtest_protocol(item, nextitem):
|
|||
|
||||
:return boolean: True if no further hook implementations should be invoked.
|
||||
"""
|
||||
pytest_runtest_protocol.firstresult = True
|
||||
|
||||
def pytest_runtest_logstart(nodeid, location):
|
||||
""" 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.
|
||||
"""
|
||||
|
||||
@hookspec_opts(firstresult=True)
|
||||
def pytest_runtest_makereport(item, call):
|
||||
""" return a :py:class:`_pytest.runner.TestReport` object
|
||||
for the given :py:class:`pytest.Item` and
|
||||
:py:class:`_pytest.runner.CallInfo`.
|
||||
"""
|
||||
pytest_runtest_makereport.firstresult = True
|
||||
|
||||
def pytest_runtest_logreport(report):
|
||||
""" 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):
|
||||
""" return a string to be displayed as header info for terminal reporting."""
|
||||
|
||||
@hookspec_opts(firstresult=True)
|
||||
def pytest_report_teststatus(report):
|
||||
""" return result-category, shortletter and verbose word for reporting."""
|
||||
pytest_report_teststatus.firstresult = True
|
||||
|
||||
def pytest_terminal_summary(terminalreporter):
|
||||
""" add additional section in terminal summary reporting. """
|
||||
|
@ -236,9 +238,9 @@ def pytest_logwarning(message, code, nodeid, fslocation):
|
|||
# doctest hooks
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@hookspec_opts(firstresult=True)
|
||||
def pytest_doctest_prepare_content(content):
|
||||
""" return processed content for a given doctest"""
|
||||
pytest_doctest_prepare_content.firstresult = True
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# error handling and internal debugging hooks
|
||||
|
|
|
@ -519,12 +519,12 @@ class Session(FSCollector):
|
|||
def _makeid(self):
|
||||
return ""
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.hookimpl_opts(tryfirst=True)
|
||||
def pytest_collectstart(self):
|
||||
if self.shouldstop:
|
||||
raise self.Interrupted(self.shouldstop)
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.hookimpl_opts(tryfirst=True)
|
||||
def pytest_runtest_logreport(self, report):
|
||||
if report.failed and not hasattr(report, 'wasxfail'):
|
||||
self._testsfailed += 1
|
||||
|
|
|
@ -24,7 +24,7 @@ def pytest_runtest_makereport(item, call):
|
|||
call.excinfo = call2.excinfo
|
||||
|
||||
|
||||
@pytest.mark.trylast
|
||||
@pytest.hookimpl_opts(trylast=True)
|
||||
def pytest_runtest_setup(item):
|
||||
if is_potential_nosetest(item):
|
||||
if isinstance(item.parent, pytest.Generator):
|
||||
|
|
|
@ -11,7 +11,7 @@ def pytest_addoption(parser):
|
|||
choices=['failed', 'all'],
|
||||
help="send failed|all info to bpaste.net pastebin service.")
|
||||
|
||||
@pytest.mark.trylast
|
||||
@pytest.hookimpl_opts(trylast=True)
|
||||
def pytest_configure(config):
|
||||
if config.option.pastebin == "all":
|
||||
tr = config.pluginmanager.getplugin('terminalreporter')
|
||||
|
|
|
@ -172,7 +172,7 @@ def pytest_configure(config):
|
|||
def pytest_sessionstart(session):
|
||||
session._fixturemanager = FixtureManager(session)
|
||||
|
||||
@pytest.mark.trylast
|
||||
@pytest.hookimpl_opts(trylast=True)
|
||||
def pytest_namespace():
|
||||
raises.Exception = pytest.fail.Exception
|
||||
return {
|
||||
|
@ -191,7 +191,7 @@ def pytestconfig(request):
|
|||
return request.config
|
||||
|
||||
|
||||
@pytest.mark.trylast
|
||||
@pytest.hookimpl_opts(trylast=True)
|
||||
def pytest_pyfunc_call(pyfuncitem):
|
||||
testfunction = pyfuncitem.obj
|
||||
if pyfuncitem._isyieldedfunction():
|
||||
|
@ -219,7 +219,7 @@ def pytest_collect_file(path, parent):
|
|||
def pytest_pycollect_makemodule(path, parent):
|
||||
return Module(path, parent)
|
||||
|
||||
@pytest.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(hookwrapper=True)
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
outcome = yield
|
||||
res = outcome.get_result()
|
||||
|
@ -1667,7 +1667,7 @@ class FixtureManager:
|
|||
self.parsefactories(plugin, nodeid)
|
||||
self._seenplugins.add(plugin)
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.hookimpl_opts(tryfirst=True)
|
||||
def pytest_configure(self, config):
|
||||
plugins = config.pluginmanager.getplugins()
|
||||
for plugin in plugins:
|
||||
|
|
|
@ -133,7 +133,7 @@ class MarkEvaluator:
|
|||
return expl
|
||||
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.hookimpl_opts(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.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(hookwrapper=True)
|
||||
def pytest_runtest_makereport(item, call):
|
||||
outcome = yield
|
||||
rep = outcome.get_result()
|
||||
|
|
|
@ -265,7 +265,7 @@ class TerminalReporter:
|
|||
def pytest_collection_modifyitems(self):
|
||||
self.report_collect(True)
|
||||
|
||||
@pytest.mark.trylast
|
||||
@pytest.hookimpl_opts(trylast=True)
|
||||
def pytest_sessionstart(self, session):
|
||||
self._sessionstarttime = time.time()
|
||||
if not self.showheader:
|
||||
|
@ -350,7 +350,7 @@ class TerminalReporter:
|
|||
indent = (len(stack) - 1) * " "
|
||||
self._tw.line("%s%s" % (indent, col))
|
||||
|
||||
@pytest.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(hookwrapper=True)
|
||||
def pytest_sessionfinish(self, exitstatus):
|
||||
outcome = yield
|
||||
outcome.get_result()
|
||||
|
|
|
@ -140,7 +140,7 @@ class TestCaseFunction(pytest.Function):
|
|||
if traceback:
|
||||
excinfo.traceback = traceback
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.hookimpl_opts(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.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(hookwrapper=True)
|
||||
def pytest_runtest_protocol(item):
|
||||
if isinstance(item, TestCaseFunction) and \
|
||||
'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.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
|
||||
|
@ -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.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
|
||||
|
|
|
@ -534,7 +534,7 @@ case we just write some informations out to a ``failures`` file::
|
|||
import pytest
|
||||
import os.path
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.hookimpl_opts(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.mark.tryfirst
|
||||
@pytest.hookimpl_opts(tryfirst=True)
|
||||
def pytest_runtest_makereport(item, call, __multicall__):
|
||||
# execute all other hooks to obtain the report object
|
||||
rep = __multicall__.execute()
|
||||
|
|
|
@ -458,7 +458,7 @@ Here is an example definition of a hook wrapper::
|
|||
|
||||
import pytest
|
||||
|
||||
@pytest.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(hookwrapper=True)
|
||||
def pytest_pyfunc_call(pyfuncitem):
|
||||
# do whatever you want before the next hook executes
|
||||
outcome = yield
|
||||
|
|
|
@ -12,6 +12,7 @@ if __name__ == '__main__': # if run as a script or by 'python -m pytest'
|
|||
# else we are imported
|
||||
|
||||
from _pytest.config import main, UsageError, _preloadplugins, cmdline
|
||||
from _pytest.core import hookspec_opts, hookimpl_opts
|
||||
from _pytest import __version__
|
||||
|
||||
_preloadplugins() # to populate pytest.* namespace so help(pytest) works
|
||||
|
|
|
@ -66,7 +66,7 @@ def check_open_files(config):
|
|||
error.append(error[0])
|
||||
raise AssertionError("\n".join(error))
|
||||
|
||||
@pytest.mark.trylast
|
||||
@pytest.hookimpl_opts(trylast=True)
|
||||
def pytest_runtest_teardown(item, __multicall__):
|
||||
item.config._basedir.chdir()
|
||||
if hasattr(item.config, '_openfiles'):
|
||||
|
|
|
@ -563,7 +563,7 @@ class TestConftestCustomization:
|
|||
b = testdir.mkdir("a").mkdir("b")
|
||||
b.join("conftest.py").write(py.code.Source("""
|
||||
import pytest
|
||||
@pytest.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(hookwrapper=True)
|
||||
def pytest_pycollect_makeitem():
|
||||
outcome = yield
|
||||
if outcome.excinfo is None:
|
||||
|
|
|
@ -192,6 +192,53 @@ class TestAddMethodOrdering:
|
|||
assert hc.nonwrappers == [he_method1_middle]
|
||||
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:
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ def test_hookvalidation_unknown(testdir):
|
|||
def test_hookvalidation_optional(testdir):
|
||||
testdir.makeconftest("""
|
||||
import pytest
|
||||
@pytest.mark.optionalhook
|
||||
@pytest.hookimpl_opts(optionalhook=True)
|
||||
def pytest_hello(xyz):
|
||||
pass
|
||||
""")
|
||||
|
|
|
@ -510,7 +510,7 @@ class TestKeywordSelection:
|
|||
""")
|
||||
testdir.makepyfile(conftest="""
|
||||
import pytest
|
||||
@pytest.mark.hookwrapper
|
||||
@pytest.hookimpl_opts(hookwrapper=True)
|
||||
def pytest_pycollect_makeitem(name):
|
||||
outcome = yield
|
||||
if name == "TestClass":
|
||||
|
|
Loading…
Reference in New Issue