Beispiel #1
0
 def test_funcarg_non_pycollectobj(self, testdir,
                                   recwarn) -> None:  # rough jstests usage
     testdir.makeconftest("""
         import pytest
         def pytest_pycollect_makeitem(collector, name, obj):
             if name == "MyClass":
                 return MyCollector.from_parent(collector, name=name)
         class MyCollector(pytest.Collector):
             def reportinfo(self):
                 return self.fspath, 3, "xyz"
     """)
     modcol = testdir.getmodulecol("""
         import pytest
         @pytest.fixture
         def arg1(request):
             return 42
         class MyClass(object):
             pass
     """)
     # this hook finds funcarg factories
     rep = runner.collect_one_node(collector=modcol)
     # TODO: Don't treat as Any.
     clscol: Any = rep.result[0]
     clscol.obj = lambda arg1: None
     clscol.funcargs = {}
     pytest._fillfuncargs(clscol)
     assert clscol.funcargs["arg1"] == 42
 def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage
     testdir.makeconftest("""
         import pytest
         def pytest_pycollect_makeitem(collector, name, obj):
             if name == "MyClass":
                 return MyCollector(name, parent=collector)
         class MyCollector(pytest.Collector):
             def reportinfo(self):
                 return self.fspath, 3, "xyz"
     """)
     modcol = testdir.getmodulecol("""
         import pytest
         @pytest.fixture
         def arg1(request):
             return 42
         class MyClass(object):
             pass
     """)
     # this hook finds funcarg factories
     rep = runner.collect_one_node(collector=modcol)
     clscol = rep.result[0]
     clscol.obj = lambda arg1: None
     clscol.funcargs = {}
     pytest._fillfuncargs(clscol)
     assert clscol.funcargs['arg1'] == 42
Beispiel #3
0
 def _perform_collect(self, args, genitems):
     if args is None:
         args = self.config.args
     self.trace("perform_collect", self, args)
     self.trace.root.indent += 1
     self._notfound = []
     self._initialpaths = set()
     self._initialparts = []
     self.items = items = []
     for arg in args:
         parts = self._parsearg(arg)
         self._initialparts.append(parts)
         self._initialpaths.add(parts[0])
     rep = collect_one_node(self)
     self.ihook.pytest_collectreport(report=rep)
     self.trace.root.indent -= 1
     if self._notfound:
         errors = []
         for arg, exc in self._notfound:
             line = "(no name %r in any of %r)" % (arg, exc.args[0])
             errors.append("not found: %s\n%s" % (arg, line))
             #XXX: test this
         raise pytest.UsageError(*errors)
     if not genitems:
         return rep.result
     else:
         if rep.passed:
             for node in rep.result:
                 self.items.extend(self.genitems(node))
         return items
Beispiel #4
0
 def _matchnodes(self, matching, names):
     if not matching or not names:
         return matching
     name = names[0]
     assert name
     nextnames = names[1:]
     resultnodes = []
     for node in matching:
         if isinstance(node, Item):
             if not names:
                 resultnodes.append(node)
             continue
         assert isinstance(node, Collector)
         rep = collect_one_node(node)
         if rep.passed:
             has_matched = False
             for x in rep.result:
                 # TODO: remove parametrized workaround once collection structure contains parametrization
                 if x.name == name or x.name.split("[")[0] == name:
                     resultnodes.extend(self.matchnodes([x], nextnames))
                     has_matched = True
             # XXX accept IDs that don't have "()" for class instances
             if not has_matched and len(rep.result) == 1 and x.name == "()":
                 nextnames.insert(0, name)
                 resultnodes.extend(self.matchnodes([x], nextnames))
         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)
     return resultnodes
Beispiel #5
0
 def test_autouse_fixture(self, testdir, recwarn):  # rough jstests usage
     testdir.makeconftest(
         """
         import pytest
         def pytest_pycollect_makeitem(collector, name, obj):
             if name == "MyClass":
                 return MyCollector.from_parent(collector, name=name)
         class MyCollector(pytest.Collector):
             def reportinfo(self):
                 return self.fspath, 3, "xyz"
     """
     )
     modcol = testdir.getmodulecol(
         """
         import pytest
         @pytest.fixture(autouse=True)
         def hello():
             pass
         @pytest.fixture
         def arg1(request):
             return 42
         class MyClass(object):
             pass
     """
     )
     # this hook finds funcarg factories
     rep = runner.collect_one_node(modcol)
     clscol = rep.result[0]
     clscol.obj = lambda: None
     clscol.funcargs = {}
     pytest._fillfuncargs(clscol)
     assert not clscol.funcargs
Beispiel #6
0
 def _matchnodes(self, matching, names):
     if not matching or not names:
         return matching
     name = names[0]
     assert name
     nextnames = names[1:]
     resultnodes = []
     for node in matching:
         if isinstance(node, pytest.Item):
             if not names:
                 resultnodes.append(node)
             continue
         assert isinstance(node, pytest.Collector)
         rep = collect_one_node(node)
         if rep.passed:
             has_matched = False
             for x in rep.result:
                 if x.name == name:
                     resultnodes.extend(self.matchnodes([x], nextnames))
                     has_matched = True
             # XXX accept IDs that don't have "()" for class instances
             if not has_matched and len(rep.result) == 1 and x.name == "()":
                 nextnames.insert(0, name)
                 resultnodes.extend(self.matchnodes([x], nextnames))
         node.ihook.pytest_collectreport(report=rep)
     return resultnodes
Beispiel #7
0
 def _perform_collect(self, args, genitems):
     if args is None:
         args = self.config.args
     self.trace("perform_collect", self, args)
     self.trace.root.indent += 1
     self._notfound = []
     initialpaths = []  # type: List[py.path.local]
     self._initial_parts = []  # type: List[Tuple[py.path.local, List[str]]]
     self.items = items = []
     for arg in args:
         fspath, parts = self._parsearg(arg)
         self._initial_parts.append((fspath, parts))
         initialpaths.append(fspath)
     self._initialpaths = frozenset(initialpaths)
     rep = collect_one_node(self)
     self.ihook.pytest_collectreport(report=rep)
     self.trace.root.indent -= 1
     if self._notfound:
         errors = []
         for arg, exc in self._notfound:
             line = "(no name {!r} in any of {!r})".format(arg, exc.args[0])
             errors.append("not found: {}\n{}".format(arg, line))
         raise UsageError(*errors)
     if not genitems:
         return rep.result
     else:
         if rep.passed:
             for node in rep.result:
                 self.items.extend(self.genitems(node))
         return items
Beispiel #8
0
 def _matchnodes(self, matching, names):
     if not matching or not names:
         return matching
     name = names[0]
     assert name
     nextnames = names[1:]
     resultnodes = []
     for node in matching:
         if isinstance(node, pytest.Item):
             if not names:
                 resultnodes.append(node)
             continue
         assert isinstance(node, pytest.Collector)
         rep = collect_one_node(node)
         if rep.passed:
             has_matched = False
             for x in rep.result:
                 if x.name == name:
                     resultnodes.extend(self.matchnodes([x], nextnames))
                     has_matched = True
             # XXX accept IDs that don't have "()" for class instances
             if not has_matched and len(rep.result) == 1 and x.name == "()":
                 nextnames.insert(0, name)
                 resultnodes.extend(self.matchnodes([x], nextnames))
         node.ihook.pytest_collectreport(report=rep)
     return resultnodes
Beispiel #9
0
 def _perform_collect(self, args, genitems):
     if args is None:
         args = self.config.args
     self.trace("perform_collect", self, args)
     self.trace.root.indent += 1
     self._notfound = []
     self._initialpaths = set()
     self._initialparts = []
     self.items = items = []
     for arg in args:
         parts = self._parsearg(arg)
         self._initialparts.append(parts)
         self._initialpaths.add(parts[0])
     rep = collect_one_node(self)
     self.ihook.pytest_collectreport(report=rep)
     self.trace.root.indent -= 1
     if self._notfound:
         errors = []
         for arg, exc in self._notfound:
             line = "(no name %r in any of %r)" % (arg, exc.args[0])
             errors.append("not found: %s\n%s" % (arg, line))
             #XXX: test this
         raise pytest.UsageError(*errors)
     if not genitems:
         return rep.result
     else:
         if rep.passed:
             for node in rep.result:
                 self.items.extend(self.genitems(node))
         return items
Beispiel #10
0
 def _matchnodes(self, matching, names):
     if not matching or not names:
         return matching
     name = names[0]
     assert name
     nextnames = names[1:]
     resultnodes = []
     for node in matching:
         if isinstance(node, Item):
             if not names:
                 resultnodes.append(node)
             continue
         assert isinstance(node, Collector)
         rep = collect_one_node(node)
         if rep.passed:
             has_matched = False
             for x in rep.result:
                 # TODO: remove parametrized workaround once collection structure contains parametrization
                 if x.name == name or x.name.split("[")[0] == name:
                     resultnodes.extend(self.matchnodes([x], nextnames))
                     has_matched = True
             # XXX accept IDs that don't have "()" for class instances
             if not has_matched and len(rep.result) == 1 and x.name == "()":
                 nextnames.insert(0, name)
                 resultnodes.extend(self.matchnodes([x], nextnames))
         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)
     return resultnodes
 def genitems(self, node):
     self.trace("genitems", node)
     if isinstance(node, nodes.Item):
         node.ihook.pytest_itemcollected(item=node)
         yield node
     else:
         assert isinstance(node, nodes.Collector)
         rep = collect_one_node(node)
         if rep.passed:
             for subnode in rep.result:
                 yield from self.genitems(subnode)
         node.ihook.pytest_collectreport(report=rep)
Beispiel #12
0
 def genitems(self, node):
     self.trace("genitems", node)
     if isinstance(node, pytest.Item):
         node.ihook.pytest_itemcollected(item=node)
         yield node
     else:
         assert isinstance(node, pytest.Collector)
         rep = collect_one_node(node)
         if rep.passed:
             for subnode in rep.result:
                 for x in self.genitems(subnode):
                     yield x
         node.ihook.pytest_collectreport(report=rep)
Beispiel #13
0
 def test_collect_result(self, testdir):
     col = testdir.getmodulecol("""
         def test_func1():
             pass
         class TestClass:
             pass
     """)
     rep = runner.collect_one_node(col)
     assert not rep.failed
     assert not rep.skipped
     assert rep.passed
     locinfo = rep.location
     assert locinfo[0] == col.fspath.basename
     assert not locinfo[1]
     assert locinfo[2] == col.fspath.basename
     res = rep.result
     assert len(res) == 2
     assert res[0].name == "test_func1"
     assert res[1].name == "TestClass"
Beispiel #14
0
 def test_collect_result(self, pytester: Pytester) -> None:
     col = pytester.getmodulecol("""
         def test_func1():
             pass
         class TestClass(object):
             pass
     """)
     rep = runner.collect_one_node(col)
     assert not rep.failed
     assert not rep.skipped
     assert rep.passed
     locinfo = rep.location
     assert locinfo[0] == col.fspath.basename
     assert not locinfo[1]
     assert locinfo[2] == col.fspath.basename
     res = rep.result
     assert len(res) == 2
     assert res[0].name == "test_func1"
     assert res[1].name == "TestClass"
Beispiel #15
0
 def _matchnodes(
     self, matching: Sequence[Union[nodes.Item, nodes.Collector]], names: List[str],
 ) -> Sequence[Union[nodes.Item, nodes.Collector]]:
     if not matching or not names:
         return matching
     name = names[0]
     assert name
     nextnames = names[1:]
     resultnodes = []  # type: List[Union[nodes.Item, nodes.Collector]]
     for node in matching:
         if isinstance(node, nodes.Item):
             if not names:
                 resultnodes.append(node)
             continue
         assert isinstance(node, nodes.Collector)
         key = (type(node), node.nodeid)
         if key in self._collection_node_cache3:
             rep = self._collection_node_cache3[key]
         else:
             rep = collect_one_node(node)
             self._collection_node_cache3[key] = rep
         if rep.passed:
             has_matched = False
             for x in rep.result:
                 # TODO: Remove parametrized workaround once collection structure contains parametrization.
                 if x.name == name or x.name.split("[")[0] == name:
                     resultnodes.extend(self.matchnodes([x], nextnames))
                     has_matched = True
             # XXX Accept IDs that don't have "()" for class instances.
             if not has_matched and len(rep.result) == 1 and x.name == "()":
                 nextnames.insert(0, name)
                 resultnodes.extend(self.matchnodes([x], nextnames))
         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)
     return resultnodes
Beispiel #16
0
 def test_funcarg_non_pycollectobj(self, testdir):  # rough jstests usage
     testdir.makeconftest("""
         import pytest
         def pytest_pycollect_makeitem(collector, name, obj):
             if name == "MyClass":
                 return MyCollector(name, parent=collector)
         class MyCollector(pytest.Collector):
             def reportinfo(self):
                 return self.fspath, 3, "xyz"
     """)
     modcol = testdir.getmodulecol("""
         def pytest_funcarg__arg1(request):
             return 42
         class MyClass:
             pass
     """)
     # this hook finds funcarg factories
     rep = runner.collect_one_node(collector=modcol)
     clscol = rep.result[0]
     clscol.obj = lambda arg1: None
     clscol.funcargs = {}
     pytest._fillfuncargs(clscol)
     assert clscol.funcargs['arg1'] == 42
Beispiel #17
0
    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
Beispiel #18
0
    def perform_collect(
        self, args: Optional[Sequence[str]] = None, genitems: bool = True
    ) -> Sequence[Union[nodes.Item, nodes.Collector]]:
        """Perform the collection phase for this session.

        This is called by the default
        :func:`pytest_collection <_pytest.hookspec.pytest_collection>` hook
        implementation; see the documentation of this hook for more details.
        For testing purposes, it may also be called directly on a fresh
        ``Session``.

        This function normally recursively expands any collectors collected
        from the session to their items, and only items are returned. For
        testing purposes, this may be suppressed by passing ``genitems=False``,
        in which case the return value contains these collectors unexpanded,
        and ``session.items`` is empty.
        """
        if args is None:
            args = self.config.args

        self.trace("perform_collect", self, args)
        self.trace.root.indent += 1

        self._notfound: List[Tuple[str, Sequence[nodes.Collector]]] = []
        self._initial_parts: List[Tuple[Path, List[str]]] = []
        self.items: List[nodes.Item] = []

        hook = self.config.hook

        items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items
        try:
            initialpaths: List[Path] = []
            for arg in args:
                fspath, parts = resolve_collection_argument(
                    self.config.invocation_params.dir,
                    arg,
                    as_pypath=self.config.option.pyargs,
                )
                self._initial_parts.append((fspath, parts))
                initialpaths.append(fspath)
            self._initialpaths = frozenset(initialpaths)
            rep = collect_one_node(self)
            self.ihook.pytest_collectreport(report=rep)
            self.trace.root.indent -= 1
            if self._notfound:
                errors = []
                for arg, cols in self._notfound:
                    line = f"(no name {arg!r} in any of {cols!r})"
                    errors.append(f"not found: {arg}\n{line}")
                raise UsageError(*errors)
            if not genitems:
                items = rep.result
            else:
                if rep.passed:
                    for node in rep.result:
                        self.items.extend(self.genitems(node))

            self.config.pluginmanager.check_pending()
            hook.pytest_collection_modifyitems(
                session=self, config=self.config, items=items
            )
        finally:
            hook.pytest_collection_finish(session=self)

        self.testscollected = len(items)
        return items