This causes `Session` documentation to be rendered again in full under
`pytester` and `testdir` in the API Reference. I tried but couldn't get
sphinx to hide it.
Since it's a pretty odd thing to have (should just use
`pytest.Session`), and I couldn't find any plugin which uses this, let's
just remove it.
`reportinfo()` is the last remaining py.path-only code path in pytest,
i.e. the last piece holding back py.path deprecation. The problem with
it is that plugins/users use it from both sides -- implementing it
(returning the value) and using it (using the return value). Dealing
with implementers is easy enough -- allow to return `os.PathLike[str]`.
But for callers who expect strictly `py.path` this will break and
there's not really a good way to provide backward compat for this.
From analyzing a corpus of 680 pytest plugins, the vast majority of
`reportinfo` appearances are implementations, and the few callers don't
actually access the path part of the return tuple.
As for test suites that might access `reportinfo` (e.g. using
`request.node.reportinfo()` or other ways), that is much harder to
survey, but from the ones I searched, I only found case
(`pytest_teamcity`, but even then it uses `str(fspath)` so is unlikely
to be affected in practice). They are better served with using
`node.location` or `node.path` directly.
Therefore, just break it and change the return type to
`str|os.PathLike[str]`.
Refs #7259.
Inline `_makeitem()` so that `self.ihook` (which is moderately
expensive) can be called only once.
Note: the removed test "test_makeitem_non_underscore" comes from an old
behavior of skipping names that start with `_` which has since been
generalized, making the test no longer relevant.
When a Python object (module/class/instance) is collected, for each name
in `obj.__dict__` (and up its MRO) the pytest_pycollect_makeitem hook is
called for potentially creating a node for it.
These Python objects have a bunch of builtin attributes that are
extremely unlikely to be collected. But due to their pervasiveness,
dispatching the hook for them ends up being mildly expensive and also
pollutes PYTEST_DEBUG=1 output and such.
Let's just ignore these attributes.
On the pandas test suite commit 04e9e0afd476b1b8bed930e47bf60e,
collect only, irrelevant lines snipped, about 5% improvement:
Before:
```
51195095 function calls (48844352 primitive calls) in 39.089 seconds
ncalls tottime percall cumtime percall filename:lineno(function)
226602/54 0.145 0.000 38.940 0.721 manager.py:90(_hookexec)
72227 0.285 0.000 20.146 0.000 python.py:424(_makeitem)
72227 0.171 0.000 16.678 0.000 python.py:218(pytest_pycollect_makeitem)
```
After:
```
48410921 function calls (46240870 primitive calls) in 36.950 seconds
ncalls tottime percall cumtime percall filename:lineno(function)
181429/54 0.113 0.000 36.777 0.681 manager.py:90(_hookexec)
27054 0.130 0.000 17.755 0.001 python.py:465(_makeitem)
27054 0.121 0.000 16.219 0.001 python.py:218(pytest_pycollect_makeitem)
```
In Python, if module A defines a name `name`, and module B does `import
name from A`, then another module C can `import name from B`.
Sometimes it is intentional -- module B is meant to "reexport" `name`.
But sometimes it is just confusion/inconsistency on where `name` should
be imported from.
mypy has a flag `--no-implicit-reexport` which puts some order into
this. A name can only be imported from a module if
1. The module defines the name
2. The module's `__all__` includes the name
3. The module imports the name as `from ... import .. as name`.
This flag is included in mypy's `--strict` flag.
I like this flag, but I realize it is a bit controversial, and in
particular item 3 above is a bit unfriendly to contributors who don't
know about it. So I didn't intend to add it to pytest.
But while investigating issue 7589 I came upon mypy issue 8754 which
causes `--no-implicit-reexport` to leak into installed libraries and
causes some unexpected typing differences *in pytest* if the user uses
this flag.
Since the diff mostly makes sense, let's just conform to it.
Mypy currently is unable to handle assigning attributes on function:
https://github.com/python/mypy/issues/2087.
pytest uses this for the outcome exceptions -- `pytest.fail.Exception`,
`pytest.exit.Exception` etc, and this is the canonical name by which they
are referred.
Initially we started working around this with type: ignores, and later
by switching e.g. `pytest.fail.Exception` with the direct exception
`Failed`. But this causes a lot of churn and is not as nice. And I also
found that some code relies on it, in skipping.py:
def pytest_configure(config):
if config.option.runxfail:
# yay a hack
import pytest
old = pytest.xfail
config._cleanup.append(lambda: setattr(pytest, "xfail", old))
def nop(*args, **kwargs):
pass
nop.Exception = xfail.Exception
setattr(pytest, "xfail", nop)
...
So it seems better to support it. Use a hack to make it work. The rest
of the commit rolls back all of the workarounds we added up to now.
`pytest.raises.Exception` also exists, but it's not used much so I kept
it as-is for now.
Hopefully in the future mypy supports this and this ugliness can be
removed.
ExitCode is used in several internal modules and hooks and so with type
annotations added, needs to be imported a lot.
_pytest.main, being the entry point, generally sits at the top of the
import tree.
So, it's not great to have ExitCode defined in _pytest.main, because it
will cause a lot of import cycles once type annotations are added (in
fact there is already one, which this change removes).
Move it to _pytest.config instead.
_pytest.main still imports ExitCode, so importing from there still
works, although external users should really be importing from `pytest`.