Skip to content

Commit

Permalink
Reset the hook proxy cache if the number of conftests changes
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Nov 22, 2016
1 parent 5ce551e commit 56e8274
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 3 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,23 @@ Changes
* Change exception raised by ``capture.DontReadFromInput.fileno()`` from ``ValueError``
to ``io.UnsupportedOperation``. Thanks `@vlad-dragos`_ for the PR.

* Fix an internal caching issue which could cause hooks from ``conftest.py`` files in
sub-directories to be called in other directories incorrectly (`#2016`_).
Thanks `@d-b-w`_ for the report and `@nicoddemus`_ for the PR.

* fix `#2013`_: turn RecordedWarning into namedtupe,
to give it a comprehensible repr while preventing unwarranted modification
* fix `#2013`_: turn RecordedWarning into ``namedtuple``
to give it a comprehensible repr while preventing unwarranted modification.

.. _@davidszotten: https://github.com/davidszotten
.. _@d-b-w: https://github.com/d-b-w
.. _@fushi: https://github.com/fushi
.. _@mattduck: https://github.com/mattduck

.. _#1512: https://github.com/pytest-dev/pytest/issues/1512
.. _#1874: https://github.com/pytest-dev/pytest/pull/1874
.. _#1952: https://github.com/pytest-dev/pytest/pull/1952
.. _#2013: https://github.com/pytest-dev/pytest/issues/2013
.. _#2016: https://github.com/pytest-dev/pytest/issues/2016


3.0.5.dev0
Expand Down
13 changes: 13 additions & 0 deletions _pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,20 @@ def pytest_runtest_logreport(self, report):
def isinitpath(self, path):
return path in self._initialpaths

def _maybe_clear_hook_proxy_cache(self):
"""
Reset the hook proxy cache if the number of conftests has changed
since the last time it was used (#2016).
"""
pm = self.config.pluginmanager
total_conftests = len(pm._conftest_plugins)
last_conftests = getattr(self, 'last_conftests', 0)
if total_conftests != last_conftests:
self._fs2hookproxy.clear()
self.last_conftests = total_conftests

def gethookproxy(self, fspath):
self._maybe_clear_hook_proxy_cache()
try:
return self._fs2hookproxy[fspath]
except KeyError:
Expand Down
1 change: 1 addition & 0 deletions _pytest/pytester.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ def _makefile(self, ext, args, kwargs):
ret = None
for name, value in items:
p = self.tmpdir.join(name).new(ext=ext)
p.dirpath().ensure_dir()
source = Source(value)

def my_totext(s, encoding="utf-8"):
Expand Down
23 changes: 23 additions & 0 deletions testing/test_conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,3 +423,26 @@ def test_some():
res = testdir.runpytest()
assert res.ret == 4
assert 'raise ValueError()' in [line.strip() for line in res.errlines]


def test_hook_proxy_cache(testdir):
"""Session's gethookproxy() could cache incorrectly conftests (#2016)."""
testdir.makepyfile(**{
'root/demo-0/test_foo1.py': "def test1(): pass",

'root/demo-a/test_foo2.py': "def test1(): pass",
'root/demo-a/conftest.py': """
def pytest_ignore_collect(path, config):
return True
""",

'root/demo-b/test_foo3.py': "def test1(): pass",
'root/demo-c/test_foo4.py': "def test1(): pass",
})
result = testdir.runpytest()
result.stdout.fnmatch_lines([
'*test_foo1.py*',
'*test_foo3.py*',
'*test_foo4.py*',
'*3 passed*',
])
22 changes: 21 additions & 1 deletion testing/test_pluginmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import os

from _pytest.config import get_config, PytestPluginManager
from _pytest.main import EXIT_NOTESTSCOLLECTED
from _pytest.main import EXIT_NOTESTSCOLLECTED, Session


@pytest.fixture
def pytestpm():
Expand Down Expand Up @@ -133,6 +134,25 @@ def pytest_plugin_registered(self):
finally:
undo()

def test_hook_proxy_cache_reset(self, testdir):
"""Importing new conftest files should reset the internal hook proxy cache (#2016)"""
config = testdir.parseconfig()
session = Session(config)
testdir.makepyfile(**{
'tests/conftest.py': '',
'tests/subdir/conftest.py': '',
})

conftest1 = testdir.tmpdir.join('tests/conftest.py')
conftest2 = testdir.tmpdir.join('tests/subdir/conftest.py')

config.pluginmanager._importconftest(conftest1)
ihook_a = session.gethookproxy(testdir.tmpdir.join('tests'))
assert ihook_a is not None
config.pluginmanager._importconftest(conftest2)
ihook_b = session.gethookproxy(testdir.tmpdir.join('tests'))
assert ihook_a is not ihook_b

def test_warn_on_deprecated_multicall(self, pytestpm):
warnings = []

Expand Down

0 comments on commit 56e8274

Please sign in to comment.