collect: python: fix `AssertionError` with broken symlinks

Fixes https://github.com/pytest-dev/pytest/issues/4782.
This commit is contained in:
Daniel Hahler 2019-02-13 17:32:04 +01:00
parent 8726be27a6
commit 407d4a0cf0
4 changed files with 48 additions and 7 deletions

View File

@ -0,0 +1 @@
Fix ``AssertionError`` with collection of broken symlinks with packages.

View File

@ -597,7 +597,12 @@ class Session(nodes.FSCollector):
yield y yield y
def _collectfile(self, path, handle_dupes=True): def _collectfile(self, path, handle_dupes=True):
assert path.isfile() assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % (
path,
path.isdir(),
path.exists(),
path.islink(),
)
ihook = self.gethookproxy(path) ihook = self.gethookproxy(path)
if not self.isinitpath(path): if not self.isinitpath(path):
if ihook.pytest_ignore_collect(path=path, config=self.config): if ihook.pytest_ignore_collect(path=path, config=self.config):

View File

@ -599,7 +599,12 @@ class Package(Module):
return proxy return proxy
def _collectfile(self, path, handle_dupes=True): def _collectfile(self, path, handle_dupes=True):
assert path.isfile() assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % (
path,
path.isdir(),
path.exists(),
path.islink(),
)
ihook = self.gethookproxy(path) ihook = self.gethookproxy(path)
if not self.isinitpath(path): if not self.isinitpath(path):
if ihook.pytest_ignore_collect(path=path, config=self.config): if ihook.pytest_ignore_collect(path=path, config=self.config):
@ -632,7 +637,8 @@ class Package(Module):
pkg_prefixes = set() pkg_prefixes = set()
for path in this_path.visit(rec=self._recurse, bf=True, sort=True): for path in this_path.visit(rec=self._recurse, bf=True, sort=True):
# We will visit our own __init__.py file, in which case we skip it. # We will visit our own __init__.py file, in which case we skip it.
if path.isfile(): is_file = path.isfile()
if is_file:
if path.basename == "__init__.py" and path.dirpath() == this_path: if path.basename == "__init__.py" and path.dirpath() == this_path:
continue continue
@ -643,12 +649,14 @@ class Package(Module):
): ):
continue continue
if path.isdir(): if is_file:
if path.join("__init__.py").check(file=1):
pkg_prefixes.add(path)
else:
for x in self._collectfile(path): for x in self._collectfile(path):
yield x yield x
elif not path.isdir():
# Broken symlink or invalid/missing file.
continue
elif path.join("__init__.py").check(file=1):
pkg_prefixes.add(path)
def _get_xunit_setup_teardown(holder, attr_name, param_obj=None): def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):

View File

@ -1186,3 +1186,30 @@ def test_collect_pkg_init_and_file_in_args(testdir):
"*2 passed in*", "*2 passed in*",
] ]
) )
@pytest.mark.skipif(
not hasattr(py.path.local, "mksymlinkto"),
reason="symlink not available on this platform",
)
@pytest.mark.parametrize("use_pkg", (True, False))
def test_collect_sub_with_symlinks(use_pkg, testdir):
sub = testdir.mkdir("sub")
if use_pkg:
sub.ensure("__init__.py")
sub.ensure("test_file.py").write("def test_file(): pass")
# Create a broken symlink.
sub.join("test_broken.py").mksymlinkto("test_doesnotexist.py")
# Symlink that gets collected.
sub.join("test_symlink.py").mksymlinkto("test_file.py")
result = testdir.runpytest("-v", str(sub))
result.stdout.fnmatch_lines(
[
"sub/test_file.py::test_file PASSED*",
"sub/test_symlink.py::test_file PASSED*",
"*2 passed in*",
]
)