Merge pull request #2127 from malinoff/fix-2124

Use session.config.hook instead of ihook. Fixes #2124
This commit is contained in:
Bruno Oliveira 2017-11-12 21:57:41 -02:00 committed by GitHub
commit 685387a43e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 77 additions and 25 deletions

View File

@ -1,13 +1,15 @@
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
import functools
import inspect import inspect
import sys import sys
import warnings import warnings
from collections import OrderedDict
import attr
import py import py
from py._code.code import FormattedExcinfo from py._code.code import FormattedExcinfo
import attr
import _pytest import _pytest
from _pytest import nodes from _pytest import nodes
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
@ -22,9 +24,6 @@ from _pytest.compat import (
from _pytest.outcomes import fail, TEST_OUTCOME from _pytest.outcomes import fail, TEST_OUTCOME
from collections import OrderedDict
def pytest_sessionstart(session): def pytest_sessionstart(session):
import _pytest.python import _pytest.python
@ -519,7 +518,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
val = fixturedef.execute(request=subrequest) val = fixturedef.execute(request=subrequest)
finally: finally:
# if fixture function failed it might have registered finalizers # if fixture function failed it might have registered finalizers
self.session._setupstate.addfinalizer(fixturedef.finish, self.session._setupstate.addfinalizer(functools.partial(fixturedef.finish, request=subrequest),
subrequest.node) subrequest.node)
return val return val
@ -573,7 +572,6 @@ class SubRequest(FixtureRequest):
self.param_index = param_index self.param_index = param_index
self.scope = scope self.scope = scope
self._fixturedef = fixturedef self._fixturedef = fixturedef
self.addfinalizer = fixturedef.addfinalizer
self._pyfuncitem = request._pyfuncitem self._pyfuncitem = request._pyfuncitem
self._fixture_values = request._fixture_values self._fixture_values = request._fixture_values
self._fixture_defs = request._fixture_defs self._fixture_defs = request._fixture_defs
@ -584,6 +582,9 @@ class SubRequest(FixtureRequest):
def __repr__(self): def __repr__(self):
return "<SubRequest %r for %r>" % (self.fixturename, self._pyfuncitem) return "<SubRequest %r for %r>" % (self.fixturename, self._pyfuncitem)
def addfinalizer(self, finalizer):
self._fixturedef.addfinalizer(finalizer)
class ScopeMismatchError(Exception): class ScopeMismatchError(Exception):
""" A fixture function tries to use a different fixture function which """ A fixture function tries to use a different fixture function which
@ -734,17 +735,17 @@ class FixtureDef:
self.argnames = getfuncargnames(func, is_method=unittest) self.argnames = getfuncargnames(func, is_method=unittest)
self.unittest = unittest self.unittest = unittest
self.ids = ids self.ids = ids
self._finalizer = [] self._finalizers = []
def addfinalizer(self, finalizer): def addfinalizer(self, finalizer):
self._finalizer.append(finalizer) self._finalizers.append(finalizer)
def finish(self): def finish(self, request):
exceptions = [] exceptions = []
try: try:
while self._finalizer: while self._finalizers:
try: try:
func = self._finalizer.pop() func = self._finalizers.pop()
func() func()
except: # noqa except: # noqa
exceptions.append(sys.exc_info()) exceptions.append(sys.exc_info())
@ -754,12 +755,15 @@ class FixtureDef:
py.builtin._reraise(*e) py.builtin._reraise(*e)
finally: finally:
ihook = self._fixturemanager.session.ihook hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
ihook.pytest_fixture_post_finalizer(fixturedef=self) hook.pytest_fixture_post_finalizer(fixturedef=self, request=request)
# even if finalization fails, we invalidate # even if finalization fails, we invalidate
# the cached fixture value # the cached fixture value and remove
# all finalizers because they may be bound methods which will
# keep instances alive
if hasattr(self, "cached_result"): if hasattr(self, "cached_result"):
del self.cached_result del self.cached_result
self._finalizers = []
def execute(self, request): def execute(self, request):
# get required arguments and register our own finish() # get required arguments and register our own finish()
@ -767,7 +771,7 @@ class FixtureDef:
for argname in self.argnames: for argname in self.argnames:
fixturedef = request._get_active_fixturedef(argname) fixturedef = request._get_active_fixturedef(argname)
if argname != "request": if argname != "request":
fixturedef.addfinalizer(self.finish) fixturedef.addfinalizer(functools.partial(self.finish, request=request))
my_cache_key = request.param_index my_cache_key = request.param_index
cached_result = getattr(self, "cached_result", None) cached_result = getattr(self, "cached_result", None)
@ -780,11 +784,11 @@ class FixtureDef:
return result return result
# we have a previous but differently parametrized fixture instance # we have a previous but differently parametrized fixture instance
# so we need to tear it down before creating a new one # so we need to tear it down before creating a new one
self.finish() self.finish(request)
assert not hasattr(self, "cached_result") assert not hasattr(self, "cached_result")
ihook = self._fixturemanager.session.ihook hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
return ihook.pytest_fixture_setup(fixturedef=self, request=request) return hook.pytest_fixture_setup(fixturedef=self, request=request)
def __repr__(self): def __repr__(self):
return ("<FixtureDef name=%r scope=%r baseid=%r >" % return ("<FixtureDef name=%r scope=%r baseid=%r >" %

View File

@ -296,7 +296,7 @@ def pytest_fixture_setup(fixturedef, request):
Stops at first non-None result, see :ref:`firstresult` """ Stops at first non-None result, see :ref:`firstresult` """
def pytest_fixture_post_finalizer(fixturedef): def pytest_fixture_post_finalizer(fixturedef, request):
""" called after fixture teardown, but before the cache is cleared so """ called after fixture teardown, but before the cache is cleared so
the fixture result cache ``fixturedef.cached_result`` can the fixture result cache ``fixturedef.cached_result`` can
still be accessed.""" still be accessed."""

View File

@ -499,8 +499,16 @@ class FSCollector(Collector):
super(FSCollector, self).__init__(name, parent, config, session) super(FSCollector, self).__init__(name, parent, config, session)
self.fspath = fspath self.fspath = fspath
def _check_initialpaths_for_relpath(self):
for initialpath in self.session._initialpaths:
if self.fspath.common(initialpath) == initialpath:
return self.fspath.relto(initialpath.dirname)
def _makeid(self): def _makeid(self):
relpath = self.fspath.relto(self.config.rootdir) relpath = self.fspath.relto(self.config.rootdir)
if not relpath:
relpath = self._check_initialpaths_for_relpath()
if os.sep != nodes.SEP: if os.sep != nodes.SEP:
relpath = relpath.replace(os.sep, nodes.SEP) relpath = relpath.replace(os.sep, nodes.SEP)
return relpath return relpath

View File

@ -56,11 +56,6 @@ def pytest_sessionfinish(session):
session._setupstate.teardown_all() session._setupstate.teardown_all()
class NodeInfo:
def __init__(self, location):
self.location = location
def pytest_runtest_protocol(item, nextitem): def pytest_runtest_protocol(item, nextitem):
item.ihook.pytest_runtest_logstart( item.ihook.pytest_runtest_logstart(
nodeid=item.nodeid, location=item.location, nodeid=item.nodeid, location=item.location,

1
changelog/2124.bugfix Normal file
View File

@ -0,0 +1 @@
``pytest_fixture_setup`` and ``pytest_fixture_post_finalizer`` hooks are now called for all ``conftest.py`` files.

1
changelog/2124.feature Normal file
View File

@ -0,0 +1 @@
``pytest_fixture_post_finalizer`` hook can now receive a ``request`` argument.

1
changelog/2775.bugfix Normal file
View File

@ -0,0 +1 @@
Fix the bug where running pytest with "--pyargs" will result in Items with empty "parent.nodeid" if run from a different root directory.

View File

@ -624,8 +624,10 @@ class TestInvocationVariants(object):
for p in search_path: for p in search_path:
monkeypatch.syspath_prepend(p) monkeypatch.syspath_prepend(p)
os.chdir('world')
# mixed module and filenames: # mixed module and filenames:
result = testdir.runpytest("--pyargs", "-v", "ns_pkg.hello", "world/ns_pkg") result = testdir.runpytest("--pyargs", "-v", "ns_pkg.hello", "ns_pkg/world")
testdir.chdir()
assert result.ret == 0 assert result.ret == 0
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
"*test_hello.py::test_hello*PASSED", "*test_hello.py::test_hello*PASSED",

View File

@ -3121,3 +3121,43 @@ class TestParameterizedSubRequest(object):
E*{1}:5 E*{1}:5
*1 failed* *1 failed*
""".format(fixfile.strpath, testfile.basename)) """.format(fixfile.strpath, testfile.basename))
def test_pytest_fixture_setup_and_post_finalizer_hook(testdir):
testdir.makeconftest("""
from __future__ import print_function
def pytest_fixture_setup(fixturedef, request):
print('ROOT setup hook called for {0} from {1}'.format(fixturedef.argname, request.node.name))
def pytest_fixture_post_finalizer(fixturedef, request):
print('ROOT finalizer hook called for {0} from {1}'.format(fixturedef.argname, request.node.name))
""")
testdir.makepyfile(**{
'tests/conftest.py': """
from __future__ import print_function
def pytest_fixture_setup(fixturedef, request):
print('TESTS setup hook called for {0} from {1}'.format(fixturedef.argname, request.node.name))
def pytest_fixture_post_finalizer(fixturedef, request):
print('TESTS finalizer hook called for {0} from {1}'.format(fixturedef.argname, request.node.name))
""",
'tests/test_hooks.py': """
from __future__ import print_function
import pytest
@pytest.fixture()
def my_fixture():
return 'some'
def test_func(my_fixture):
print('TEST test_func')
assert my_fixture == 'some'
"""
})
result = testdir.runpytest("-s")
assert result.ret == 0
result.stdout.fnmatch_lines([
"*TESTS setup hook called for my_fixture from test_func*",
"*ROOT setup hook called for my_fixture from test_func*",
"*TEST test_func*",
"*TESTS finalizer hook called for my_fixture from test_func*",
"*ROOT finalizer hook called for my_fixture from test_func*",
])