This indicates at least for people using type checkers that these
classes are not designed for inheritance and we make no stability
guarantees regarding inheritance of them.
Currently this doesn't show up in the docs. Sphinx does actually support
`@final`, however it only works when imported directly from `typing`,
while we import from `_pytest.compat`.
In the future there might also be a `@sealed` decorator which would
cover some more cases.
For decorated functions, the lineno of the FunctionDef AST node points
to the `def` line, not to the first decorator line. On the other hand,
in code objects, the `co_firstlineno` points to the first decorator
line.
Assertion rewriting inserts some imports to code it rewrites. The
imports are inserted at the lineno of the first statement in the AST. In
turn, the code object compiled from the rewritten AST uses the lineno of
the first statement (which is the first inserted import).
This means that given a module like this,
```py
@foo
@bar
def baz(): pass
```
the lineno of the code object without assertion rewriting
(`--assertion=plain`) is 1, but with assertion rewriting it is 3.
And *this* causes some issues for the exception repr when e.g. the
decorator line is invalid and raises during collection. The code becomes
confused and crashes with
INTERNALERROR> File "_pytest/_code/code.py", line 638, in get_source
INTERNALERROR> lines.append(space_prefix + source.lines[line_index].strip())
INTERNALERROR> IndexError: list index out of range
Fix it by special casing decorators. Maybe there are other cases like
this but off hand I can't think of another Python construct where the
lineno of the item would be after its first line, and this is the only
such issue we have had reported.
When a name is exported from `pytest`, prefer to refer to it by that
rather than its `_pytest` import path. It is shorter and more
appropriate in user-facing documentation (although that's not really
visible).
Our plan is to expose more names for typing purposes, in which can this
could be more comprehensive.
Warnings are a central part of Python, so much that Python itself has
command-line and environtment variables to handle warnings.
By moving the concept of warning handling into Config, it becomes natural to
filter warnings issued as early as possible, even before the "_pytest.warnings"
plugin is given a chance to spring into action. This also avoids the weird
coupling between config and the warnings plugin that was required before.
Fix#6681Fix#2891Fix#7620Fix#7626Close#7649
Co-authored-by: Ran Benita <ran@unusedvar.com>
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 ff8b7884e8 NOTSET was changed to a
singleton enum, which ended up unexpectedly triggering a code path in ID
generation which checks for `isinstance(Enum)`.
Add an explicit case for it, which is not too bad anyway.
This is a more sensible interface for matchnodes.
This also fixes a sort-of bug where a recursive call to matchnodes
raises NoMatch which would terminate the entire tree, even if other
branches may find a match. Though I don't think it's actually possible.
This reverts commit f10ab021e2.
The commit was good in that it removed a non-trivial amount of code
duplication. However it was done in the wrong layer (nodes.py) and split
up a major part of the collection (the filesystem traversal) to a
separate class making it harder to understand.
We should try to reduce the duplication, but in a more appropriate
manner.
The path part of a `<path>::part1::part2` style collection argument must
be a file, not a directory.
Previously this crashed with an uncool assert "invalid arg".
The `CaptureManager.global_and_fixture_disabled()` context manager (and
`CaptureFixture.disabled()` which calls it) did `suspend(); ...;
resume()` but if the capturing was already suspended, the `resume()`
would resume it when it shouldn't.
This caused caused some messages to be swallowed when `--log-cli` is
used because it uses `global_and_fixture_disabled` when capturing is not
necessarily resumed.
This fixes an issue where pylint complains about missing implementations
of abstract methods in subclasses of `File` which only override
`collect()` (as they should).
It is also cleaner and makes sense, these methods really don't need to
be overridden.
The previous methods defined directly on `FSCollector` and `Package` are
deprecated, to be removed in pytest 7.
See commits e2934c3f8c and
f10ab021e2 for reference.
Didn't call it absolute or absolute_path to avoid conflicts with
possible variable names.
Didn't call it abspath to avoid confusion with os.path.abspath.
This makes mypy raise an error whenever it detects code which is
statically unreachable, e.g.
x: int
if isinstance(x, str):
... # Statement is unreachable [unreachable]
This is really neat and finds quite a few logic and typing bugs.
Sometimes the code is intentionally unreachable in terms of types, e.g.
raising TypeError when a function is given an argument with a wrong
type. In these cases a `type: ignore[unreachable]` is needed, but I
think it's a nice code hint.
This prevents referring to a generic type without filling in its generic
type parameters.
The FixtureDef typing might need some more refining in the future.
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.
We barely use it; the couple places that do are not really worth the
extra dependency, I think the code is clearer without it.
Also simplifies one (regular) itertools usage.
Also improves a check and an error message in `pytest.raises`.
Part of the effort to reduce dependency on the py library.
Besides that, py.xml implements its own XML serialization which is
pretty scary.
I tried to keep the code with minimal changes (though it could use some
cleanups). The differences in behavior I have noticed are:
- Attributes in the output are not sorted.
- Some unneeded escaping is no longer performed, for example escaping
`"` to `"` in a text node.
1. Remove sys.maxunicode check & comment. Nowadays it is always a
constant 0x10ffff.
2. Pre-generate the pattern. Possible due to 1.
3. Compile the regex lazily. No reason to pay startup cost for it.
4. Add docstring in particular to explain a subtle point.
`os.scandir()`, introduced in Python 3.5, is much faster than
`os.listdir()`. See https://www.python.org/dev/peps/pep-0471/.
It also has a `DirEntry` which can be used to further reduce syscalls in
some cases.
Part of reducing dependency on `py`. Also enables upcoming improvements.
In cases where there are simpler alternatives (in tests), I used those.
What's left are a couple of uses in `_pytest.main` and `_pytest.python`
and they only have modest requirements, so all of the featureful code
from py is not needed.
The previous typing had an object passed to the user, which they can't
do anything with without asserting, which is inconvenient. Change it to
Any instead.
Note that what comes *back* to pytest (the return value) should be an
`object`, because we want to handle arbitrary objects without assuming
anything about them.
If a test runtest phase (not setup) dynamically adds a pytest.mark.xfail
mark to the item, it should be respected, but it wasn't. This regressed
in 3e6fe92b7e (not released).
Fix it by just always refreshing the mark if needed. This is mostly what
was done before but in a more roundabout way.
eval() is used for evaluating string conditions in skipif/xfail e.g.
@pytest.mark.skipif("1 == 0")
This is the only code that uses `_pytest._code.compile()`, so removing
its last use enables us to remove it entirely.
In this case it doesn't add much. Plain compile() gives a good enough
error message.
For regular exceptions, the message is the same.
For SyntaxError exceptions, e.g. "1 ==", the previous code adds a little
bit of useful context:
```
invalid syntax (skipping.py:108>, line 1)
The above exception was the direct cause of the following exception:
1 ==
^
(code was compiled probably from here: <0-codegen /pytest/src/_pytest/skipping.py:108>) (line 1)
During handling of the above exception, another exception occurred:
Error evaluating 'skipif' condition
1 ==
^
SyntaxError: invalid syntax
```
The new code loses it:
```
unexpected EOF while parsing (<skipif condition>, line 1)
During handling of the above exception, another exception occurred:
Error evaluating 'skipif' condition
1 ==
^
SyntaxError: invalid syntax
```
Since the old message is a minor improvement to an unlikely error
condition in a deprecated feature, I think it is not worth all the code
that it requires.
This has been there since as far as the git history goes (2007), is not
covered by any test, and says "Buggy python version consider upgrading".
Hopefully everyone have upgraded...
Setting log_level via the CLI or .ini will control the log level of the
report that is dumped upon failure of a test.
If caplog modified the log level during the execution of that test, it
should not impact the level that is displayed upon failure in the
"captured log report" section.
[
ran:
- rebased
- reused handler
- changed store keys also to "caplog_handler_*"
- added changelog
all bugs are mine :)
]
There is no need to do the XPASS check here, pytest_runtest_makereport
already handled that (the current handling there is dead code).
All the hook needs to do is refresh the xfail evaluation if needed, and
check the NOTRUN condition again.
Previously, skipif/xfail marks were evaluated using a `MarkEvaluator`
class. I found this class very difficult to understand.
Instead of `MarkEvaluator`, rewrite using straight functions which are
hopefully easier to follow.
I tried to keep the semantics exactly as before, except improving a few
error messages.
This type was actually in `_pytest.skipping` previously, but was moved to
`_pytest.mark.evaluate` in cf40c0743c.
I think the previous location was more appropriate, because the
`MarkEvaluator` is not a generic mark facility, it is explicitly and
exclusively used by the `skipif` and `xfail` marks to evaluate their
particular set of arguments. So it is better to put it in the plugin
code.
Putting `skipping` related functionality into the core `_pytest.mark`
module also causes some import cycles which we can avoid.
`@pytest.mark.xfail` is meant to work with arbitrary items, and there is
a test `test_mark_xfail_item` which verifies this.
However, the code for some reason uses `pytest_pyfunc_call` for the
call phase check, which only works for Function items. The test
mentioned above only passed "accidentally" because the
`pytest_runtest_makereport` hook also runs a `evalxfail.istrue()` which
triggers and evaluation, but conceptually it shouldn't do that.
Change to `pytest_runtest_call` to make the xfail checking properly
generic.
While working on improving the documentation of the
`pytest_runtest_setup` hook, I came up with this text:
> Called to perform the setup phase of the test item.
>
> The default implementation runs ``setup()`` on item and all of its
> parents (which haven't been setup yet). This includes obtaining the
> values of fixtures required by the item (which haven't been obtained
> yet).
But upon closer inspection I noticed this line at the start of
`SetupState.prepare` (which is what does the actual work for
`pytest_runtest_setup`):
self._teardown_towards(needed_collectors)
which implies that the setup phase of one item might trigger teardowns
of *previous* items. This complicates the simple explanation. It also
seems like a completely undesirable thing to do, because it breaks
isolation between tests -- e.g. a failed teardown of one item shouldn't
cause the failure of some other items just because it happens to run
after it.
So the first thing I tried was to remove that line and see if anything
breaks -- nothing did. At least pytest's own test suite runs fine. So
maybe it's just dead code?