Skip to content

Commit 32c6d4f

Browse files
authored
Merge pull request #4738 from pstradomski/master
Fix "ValueError: Plugin already registered" exceptions when running in build directories that symlink to actual source.
2 parents b4b2f58 + 391dc54 commit 32c6d4f

File tree

3 files changed

+41
-1
lines changed

3 files changed

+41
-1
lines changed

changelog/526.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix "ValueError: Plugin already registered" exceptions when running in build directories that symlink to actual source.

src/_pytest/config/__init__.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,10 @@ def _getconftestmodules(self, path):
408408
continue
409409
conftestpath = parent.join("conftest.py")
410410
if conftestpath.isfile():
411-
mod = self._importconftest(conftestpath)
411+
# Use realpath to avoid loading the same conftest twice
412+
# with build systems that create build directories containing
413+
# symlinks to actual files.
414+
mod = self._importconftest(conftestpath.realpath())
412415
clist.append(mod)
413416
self._dirpath2confmods[directory] = clist
414417
return clist

testing/test_conftest.py

+36
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,42 @@ def fixture():
244244
assert result.ret == EXIT_OK
245245

246246

247+
@pytest.mark.skipif(
248+
not hasattr(py.path.local, "mksymlinkto"),
249+
reason="symlink not available on this platform",
250+
)
251+
def test_conftest_symlink_files(testdir):
252+
"""Check conftest.py loading when running in directory with symlinks."""
253+
real = testdir.tmpdir.mkdir("real")
254+
source = {
255+
"app/test_foo.py": "def test1(fixture): pass",
256+
"app/__init__.py": "",
257+
"app/conftest.py": textwrap.dedent(
258+
"""
259+
import pytest
260+
261+
print("conftest_loaded")
262+
263+
@pytest.fixture
264+
def fixture():
265+
print("fixture_used")
266+
"""
267+
),
268+
}
269+
testdir.makepyfile(**{"real/%s" % k: v for k, v in source.items()})
270+
271+
# Create a build directory that contains symlinks to actual files
272+
# but doesn't symlink actual directories.
273+
build = testdir.tmpdir.mkdir("build")
274+
build.mkdir("app")
275+
for f in source:
276+
build.join(f).mksymlinkto(real.join(f))
277+
build.chdir()
278+
result = testdir.runpytest("-vs", "app/test_foo.py")
279+
result.stdout.fnmatch_lines(["*conftest_loaded*", "PASSED"])
280+
assert result.ret == EXIT_OK
281+
282+
247283
def test_no_conftest(testdir):
248284
testdir.makeconftest("assert 0")
249285
result = testdir.runpytest("--noconftest")

0 commit comments

Comments
 (0)