Skip to content

Commit 56e8274

Browse files
committed
Reset the hook proxy cache if the number of conftests changes
pytest-dev#2016
1 parent 5ce551e commit 56e8274

File tree

5 files changed

+65
-3
lines changed

5 files changed

+65
-3
lines changed

CHANGELOG.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,23 @@ Changes
2727
* Change exception raised by ``capture.DontReadFromInput.fileno()`` from ``ValueError``
2828
to ``io.UnsupportedOperation``. Thanks `@vlad-dragos`_ for the PR.
2929

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

31-
* fix `#2013`_: turn RecordedWarning into namedtupe,
32-
to give it a comprehensible repr while preventing unwarranted modification
34+
* fix `#2013`_: turn RecordedWarning into ``namedtuple``
35+
to give it a comprehensible repr while preventing unwarranted modification.
3336

3437
.. _@davidszotten: https://github.com/davidszotten
38+
.. _@d-b-w: https://github.com/d-b-w
3539
.. _@fushi: https://github.com/fushi
3640
.. _@mattduck: https://github.com/mattduck
3741

3842
.. _#1512: https://github.com/pytest-dev/pytest/issues/1512
3943
.. _#1874: https://github.com/pytest-dev/pytest/pull/1874
4044
.. _#1952: https://github.com/pytest-dev/pytest/pull/1952
4145
.. _#2013: https://github.com/pytest-dev/pytest/issues/2013
46+
.. _#2016: https://github.com/pytest-dev/pytest/issues/2016
4247

4348

4449
3.0.5.dev0

_pytest/main.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,20 @@ def pytest_runtest_logreport(self, report):
561561
def isinitpath(self, path):
562562
return path in self._initialpaths
563563

564+
def _maybe_clear_hook_proxy_cache(self):
565+
"""
566+
Reset the hook proxy cache if the number of conftests has changed
567+
since the last time it was used (#2016).
568+
"""
569+
pm = self.config.pluginmanager
570+
total_conftests = len(pm._conftest_plugins)
571+
last_conftests = getattr(self, 'last_conftests', 0)
572+
if total_conftests != last_conftests:
573+
self._fs2hookproxy.clear()
574+
self.last_conftests = total_conftests
575+
564576
def gethookproxy(self, fspath):
577+
self._maybe_clear_hook_proxy_cache()
565578
try:
566579
return self._fs2hookproxy[fspath]
567580
except KeyError:

_pytest/pytester.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ def _makefile(self, ext, args, kwargs):
481481
ret = None
482482
for name, value in items:
483483
p = self.tmpdir.join(name).new(ext=ext)
484+
p.dirpath().ensure_dir()
484485
source = Source(value)
485486

486487
def my_totext(s, encoding="utf-8"):

testing/test_conftest.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,3 +423,26 @@ def test_some():
423423
res = testdir.runpytest()
424424
assert res.ret == 4
425425
assert 'raise ValueError()' in [line.strip() for line in res.errlines]
426+
427+
428+
def test_hook_proxy_cache(testdir):
429+
"""Session's gethookproxy() could cache incorrectly conftests (#2016)."""
430+
testdir.makepyfile(**{
431+
'root/demo-0/test_foo1.py': "def test1(): pass",
432+
433+
'root/demo-a/test_foo2.py': "def test1(): pass",
434+
'root/demo-a/conftest.py': """
435+
def pytest_ignore_collect(path, config):
436+
return True
437+
""",
438+
439+
'root/demo-b/test_foo3.py': "def test1(): pass",
440+
'root/demo-c/test_foo4.py': "def test1(): pass",
441+
})
442+
result = testdir.runpytest()
443+
result.stdout.fnmatch_lines([
444+
'*test_foo1.py*',
445+
'*test_foo3.py*',
446+
'*test_foo4.py*',
447+
'*3 passed*',
448+
])

testing/test_pluginmanager.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import os
55

66
from _pytest.config import get_config, PytestPluginManager
7-
from _pytest.main import EXIT_NOTESTSCOLLECTED
7+
from _pytest.main import EXIT_NOTESTSCOLLECTED, Session
8+
89

910
@pytest.fixture
1011
def pytestpm():
@@ -133,6 +134,25 @@ def pytest_plugin_registered(self):
133134
finally:
134135
undo()
135136

137+
def test_hook_proxy_cache_reset(self, testdir):
138+
"""Importing new conftest files should reset the internal hook proxy cache (#2016)"""
139+
config = testdir.parseconfig()
140+
session = Session(config)
141+
testdir.makepyfile(**{
142+
'tests/conftest.py': '',
143+
'tests/subdir/conftest.py': '',
144+
})
145+
146+
conftest1 = testdir.tmpdir.join('tests/conftest.py')
147+
conftest2 = testdir.tmpdir.join('tests/subdir/conftest.py')
148+
149+
config.pluginmanager._importconftest(conftest1)
150+
ihook_a = session.gethookproxy(testdir.tmpdir.join('tests'))
151+
assert ihook_a is not None
152+
config.pluginmanager._importconftest(conftest2)
153+
ihook_b = session.gethookproxy(testdir.tmpdir.join('tests'))
154+
assert ihook_a is not ihook_b
155+
136156
def test_warn_on_deprecated_multicall(self, pytestpm):
137157
warnings = []
138158

0 commit comments

Comments
 (0)