def test_visit_ignores_errors(tmp_path: Path) -> None: symlink_or_skip("recursive", tmp_path / "recursive") tmp_path.joinpath("foo").write_bytes(b"") tmp_path.joinpath("bar").write_bytes(b"") assert [ entry.name for entry in visit(str(tmp_path), recurse=lambda entry: False) ] == ["bar", "foo"]
def test_visit_ignores_errors(tmpdir) -> None: symlink_or_skip("recursive", tmpdir.join("recursive")) tmpdir.join("foo").write_binary(b"") tmpdir.join("bar").write_binary(b"") assert [ entry.name for entry in visit(tmpdir, recurse=lambda entry: False) ] == [ "bar", "foo", ]
def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: from _pytest.python import Package # Keep track of any collected nodes in here, so we don't duplicate fixtures. node_cache1: Dict[py.path.local, Sequence[nodes.Collector]] = {} node_cache2: Dict[ Tuple[Type[nodes.Collector], py.path.local], nodes.Collector ] = ({}) # Keep track of any collected collectors in matchnodes paths, so they # are not collected more than once. matchnodes_cache: Dict[Tuple[Type[nodes.Collector], str], CollectReport] = ({}) # Dirnames of pkgs with dunder-init files. pkg_roots: Dict[str, Package] = {} for argpath, names in self._initial_parts: self.trace("processing argument", (argpath, names)) self.trace.root.indent += 1 # Start with a Session root, and delve to argpath item (dir or file) # and stack all Packages found on the way. # No point in finding packages when collecting doctests. if not self.config.getoption("doctestmodules", False): pm = self.config.pluginmanager confcutdir = pm._confcutdir for parent in (argpath, *argpath.parents): if confcutdir and parent in confcutdir.parents: break if parent.is_dir(): pkginit = py.path.local(parent / "__init__.py") if pkginit.isfile() and pkginit not in node_cache1: col = self._collectfile(pkginit, handle_dupes=False) if col: if isinstance(col[0], Package): pkg_roots[str(parent)] = col[0] node_cache1[col[0].fspath] = [col[0]] # If it's a directory argument, recurse and look for any Subpackages. # Let the Package collector deal with subnodes, don't collect here. if argpath.is_dir(): assert not names, "invalid arg {!r}".format((argpath, names)) seen_dirs: Set[py.path.local] = set() for direntry in visit(str(argpath), self._recurse): if not direntry.is_file(): continue path = py.path.local(direntry.path) dirpath = path.dirpath() if dirpath not in seen_dirs: # Collect packages first. seen_dirs.add(dirpath) pkginit = dirpath.join("__init__.py") if pkginit.exists(): for x in self._collectfile(pkginit): yield x if isinstance(x, Package): pkg_roots[str(dirpath)] = x if str(dirpath) in pkg_roots: # Do not collect packages here. continue for x in self._collectfile(path): key = (type(x), x.fspath) if key in node_cache2: yield node_cache2[key] else: node_cache2[key] = x yield x else: assert argpath.is_file() argpath_ = py.path.local(argpath) if argpath_ in node_cache1: col = node_cache1[argpath_] else: collect_root = pkg_roots.get(argpath_.dirname, self) col = collect_root._collectfile(argpath_, handle_dupes=False) if col: node_cache1[argpath_] = col matching = [] work: List[ Tuple[Sequence[Union[nodes.Item, nodes.Collector]], Sequence[str]] ] = [(col, names)] while work: self.trace("matchnodes", col, names) self.trace.root.indent += 1 matchnodes, matchnames = work.pop() for node in matchnodes: if not matchnames: matching.append(node) continue if not isinstance(node, nodes.Collector): continue key = (type(node), node.nodeid) if key in matchnodes_cache: rep = matchnodes_cache[key] else: rep = collect_one_node(node) matchnodes_cache[key] = rep if rep.passed: submatchnodes = [] for r in rep.result: # TODO: Remove parametrized workaround once collection structure contains # parametrization. if ( r.name == matchnames[0] or r.name.split("[")[0] == matchnames[0] ): submatchnodes.append(r) if submatchnodes: work.append((submatchnodes, matchnames[1:])) # XXX Accept IDs that don't have "()" for class instances. elif len(rep.result) == 1 and rep.result[0].name == "()": work.append((rep.result, matchnames)) else: # Report collection failures here to avoid failing to run some test # specified in the command line because the module could not be # imported (#134). node.ihook.pytest_collectreport(report=rep) self.trace("matchnodes finished -> ", len(matching), "nodes") self.trace.root.indent -= 1 if not matching: report_arg = "::".join((str(argpath), *names)) self._notfound.append((report_arg, col)) continue # If __init__.py was the only file requested, then the matched # node will be the corresponding Package (by default), and the # first yielded item will be the __init__ Module itself, so # just use that. If this special case isn't taken, then all the # files in the package will be yielded. if argpath.name == "__init__.py" and isinstance(matching[0], Package): try: yield next(iter(matching[0].collect())) except StopIteration: # The package collects nothing with only an __init__.py # file in it, which gets ignored by the default # "python_files" option. pass continue yield from matching self.trace.root.indent -= 1
def _collect( self, argpath: py.path.local, names: List[str] ) -> Iterator[Union[nodes.Item, nodes.Collector]]: from _pytest.python import Package # Start with a Session root, and delve to argpath item (dir or file) # and stack all Packages found on the way. # No point in finding packages when collecting doctests. if not self.config.getoption("doctestmodules", False): pm = self.config.pluginmanager for parent in reversed(argpath.parts()): if pm._confcutdir and pm._confcutdir.relto(parent): break if parent.isdir(): pkginit = parent.join("__init__.py") if pkginit.isfile(): if pkginit not in self._collection_node_cache1: col = self._collectfile(pkginit, handle_dupes=False) if col: if isinstance(col[0], Package): self._collection_pkg_roots[str(parent)] = col[0] # Always store a list in the cache, matchnodes expects it. self._collection_node_cache1[col[0].fspath] = [col[0]] # If it's a directory argument, recurse and look for any Subpackages. # Let the Package collector deal with subnodes, don't collect here. if argpath.check(dir=1): assert not names, "invalid arg {!r}".format((argpath, names)) seen_dirs = set() # type: Set[py.path.local] for direntry in visit(str(argpath), self._recurse): if not direntry.is_file(): continue path = py.path.local(direntry.path) dirpath = path.dirpath() if dirpath not in seen_dirs: # Collect packages first. seen_dirs.add(dirpath) pkginit = dirpath.join("__init__.py") if pkginit.exists(): for x in self._collectfile(pkginit): yield x if isinstance(x, Package): self._collection_pkg_roots[str(dirpath)] = x if str(dirpath) in self._collection_pkg_roots: # Do not collect packages here. continue for x in self._collectfile(path): key = (type(x), x.fspath) if key in self._collection_node_cache2: yield self._collection_node_cache2[key] else: self._collection_node_cache2[key] = x yield x else: assert argpath.check(file=1) if argpath in self._collection_node_cache1: col = self._collection_node_cache1[argpath] else: collect_root = self._collection_pkg_roots.get(argpath.dirname, self) col = collect_root._collectfile(argpath, handle_dupes=False) if col: self._collection_node_cache1[argpath] = col m = self.matchnodes(col, names) # If __init__.py was the only file requested, then the matched node will be # the corresponding Package, and the first yielded item will be the __init__ # Module itself, so just use that. If this special case isn't taken, then all # the files in the package will be yielded. if argpath.basename == "__init__.py": assert isinstance(m[0], nodes.Collector) try: yield next(iter(m[0].collect())) except StopIteration: # The package collects nothing with only an __init__.py # file in it, which gets ignored by the default # "python_files" option. pass return yield from m