Merge branch 'main' into okken_12231_xfail_tb

This commit is contained in:
Brian Okken 2024-04-28 12:59:00 -07:00 committed by GitHub
commit a497a2373d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 57 additions and 59 deletions

View File

@ -10,12 +10,7 @@ repos:
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
- id: fix-encoding-pragma
args: [--remove]
- id: check-yaml - id: check-yaml
- id: debug-statements
exclude: _pytest/(debugging|hookspec).py
language_version: python3
- repo: https://github.com/adamchainz/blacken-docs - repo: https://github.com/adamchainz/blacken-docs
rev: 1.16.0 rev: 1.16.0
hooks: hooks:
@ -50,13 +45,13 @@ repos:
additional_dependencies: ["tox>=4.9"] additional_dependencies: ["tox>=4.9"]
- repo: local - repo: local
hooks: hooks:
- id: pylint - id: pylint
name: pylint name: pylint
entry: pylint entry: pylint
language: system language: system
types: [python] types: [python]
args: ["-rn", "-sn", "--fail-on=I"] args: ["-rn", "-sn", "--fail-on=I"]
stages: [manual] stages: [manual]
- id: rst - id: rst
name: rst name: rst
entry: rst-lint --encoding utf-8 entry: rst-lint --encoding utf-8

1
changelog/12153.doc.rst Normal file
View File

@ -0,0 +1 @@
Documented using :envvar:`PYTEST_VERSION` to detect if code is running from within a pytest run.

View File

@ -0,0 +1 @@
Fixed progress percentages (the ``[ 87%]`` at the edge of the screen) sometimes not aligning correctly when running with pytest-xdist ``-n``.

View File

@ -405,35 +405,20 @@ Detect if running from within a pytest run
Usually it is a bad idea to make application code Usually it is a bad idea to make application code
behave differently if called from a test. But if you behave differently if called from a test. But if you
absolutely must find out if your application code is absolutely must find out if your application code is
running from a test you can do something like this: running from a test you can do this:
.. code-block:: python .. code-block:: python
# content of your_module.py import os
_called_from_test = False if os.environ.get("PYTEST_VERSION") is not None:
# Things you want to to do if your code is called by pytest.
.. code-block:: python
# content of conftest.py
def pytest_configure(config):
your_module._called_from_test = True
and then check for the ``your_module._called_from_test`` flag:
.. code-block:: python
if your_module._called_from_test:
# called from within a test run
... ...
else: else:
# called "normally" # Things you want to to do if your code is not called by pytest.
... ...
accordingly in your application.
Adding info to test report header Adding info to test report header
-------------------------------------------------------------- --------------------------------------------------------------

View File

@ -107,6 +107,7 @@ select = [
"PLE", # pylint error "PLE", # pylint error
"PLW", # pylint warning "PLW", # pylint warning
"PLR1714", # Consider merging multiple comparisons "PLR1714", # Consider merging multiple comparisons
"T100", # flake8-debugger
] ]
ignore = [ ignore = [
# bugbear ignore # bugbear ignore

View File

@ -1,4 +1,5 @@
# mypy: allow-untyped-defs # mypy: allow-untyped-defs
# ruff: noqa: T100
"""Interactive debugging with PDB, the Python Debugger.""" """Interactive debugging with PDB, the Python Debugger."""
import argparse import argparse

View File

@ -1,4 +1,5 @@
# mypy: allow-untyped-defs # mypy: allow-untyped-defs
# ruff: noqa: T100
"""Hook specifications for pytest plugins which are invoked by pytest itself """Hook specifications for pytest plugins which are invoked by pytest itself
and by builtin plugins.""" and by builtin plugins."""

View File

@ -180,7 +180,7 @@ def pytest_runtest_call(item: Item) -> None:
assert e.__traceback__ is not None assert e.__traceback__ is not None
# Skip *this* frame # Skip *this* frame
sys.last_traceback = e.__traceback__.tb_next sys.last_traceback = e.__traceback__.tb_next
raise e raise
def pytest_runtest_teardown(item: Item, nextitem: Optional[Item]) -> None: def pytest_runtest_teardown(item: Item, nextitem: Optional[Item]) -> None:
@ -512,7 +512,7 @@ class SetupState:
col.setup() col.setup()
except TEST_OUTCOME as exc: except TEST_OUTCOME as exc:
self.stack[col] = (self.stack[col][0], exc) self.stack[col] = (self.stack[col][0], exc)
raise exc raise
def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None: def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None:
"""Attach a finalizer to the given node. """Attach a finalizer to the given node.

View File

@ -441,7 +441,7 @@ class TerminalReporter:
char = {"xfailed": "x", "skipped": "s"}.get(char, char) char = {"xfailed": "x", "skipped": "s"}.get(char, char)
return char in self.reportchars return char in self.reportchars
def write_fspath_result(self, nodeid: str, res, **markup: bool) -> None: def write_fspath_result(self, nodeid: str, res: str, **markup: bool) -> None:
fspath = self.config.rootpath / nodeid.split("::")[0] fspath = self.config.rootpath / nodeid.split("::")[0]
if self.currentfspath is None or fspath != self.currentfspath: if self.currentfspath is None or fspath != self.currentfspath:
if self.currentfspath is not None and self._show_progress_info: if self.currentfspath is not None and self._show_progress_info:
@ -574,10 +574,11 @@ class TerminalReporter:
def pytest_runtest_logstart( def pytest_runtest_logstart(
self, nodeid: str, location: Tuple[str, Optional[int], str] self, nodeid: str, location: Tuple[str, Optional[int], str]
) -> None: ) -> None:
fspath, lineno, domain = location
# Ensure that the path is printed before the # Ensure that the path is printed before the
# 1st test of a module starts running. # 1st test of a module starts running.
if self.showlongtestinfo: if self.showlongtestinfo:
line = self._locationline(nodeid, *location) line = self._locationline(nodeid, fspath, lineno, domain)
self.write_ensure_prefix(line, "") self.write_ensure_prefix(line, "")
self.flush() self.flush()
elif self.showfspath: elif self.showfspath:
@ -600,7 +601,6 @@ class TerminalReporter:
if not letter and not word: if not letter and not word:
# Probably passed setup/teardown. # Probably passed setup/teardown.
return return
running_xdist = hasattr(rep, "node")
if markup is None: if markup is None:
was_xfail = hasattr(report, "wasxfail") was_xfail = hasattr(report, "wasxfail")
if rep.passed and not was_xfail: if rep.passed and not was_xfail:
@ -613,11 +613,20 @@ class TerminalReporter:
markup = {"yellow": True} markup = {"yellow": True}
else: else:
markup = {} markup = {}
self._progress_nodeids_reported.add(rep.nodeid)
if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0: if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0:
self._tw.write(letter, **markup) self._tw.write(letter, **markup)
# When running in xdist, the logreport and logfinish of multiple
# items are interspersed, e.g. `logreport`, `logreport`,
# `logfinish`, `logfinish`. To avoid the "past edge" calculation
# from getting confused and overflowing (#7166), do the past edge
# printing here and not in logfinish, except for the 100% which
# should only be printed after all teardowns are finished.
if self._show_progress_info and not self._is_last_item:
self._write_progress_information_if_past_edge()
else: else:
self._progress_nodeids_reported.add(rep.nodeid)
line = self._locationline(rep.nodeid, *rep.location) line = self._locationline(rep.nodeid, *rep.location)
running_xdist = hasattr(rep, "node")
if not running_xdist: if not running_xdist:
self.write_ensure_prefix(line, word, **markup) self.write_ensure_prefix(line, word, **markup)
if rep.skipped or hasattr(report, "wasxfail"): if rep.skipped or hasattr(report, "wasxfail"):
@ -657,39 +666,29 @@ class TerminalReporter:
assert self._session is not None assert self._session is not None
return len(self._progress_nodeids_reported) == self._session.testscollected return len(self._progress_nodeids_reported) == self._session.testscollected
def pytest_runtest_logfinish(self, nodeid: str) -> None: @hookimpl(wrapper=True)
assert self._session def pytest_runtestloop(self) -> Generator[None, object, object]:
result = yield
# Write the final/100% progress -- deferred until the loop is complete.
if ( if (
self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0 self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0
and self._show_progress_info and self._show_progress_info
and self._progress_nodeids_reported
): ):
if self._show_progress_info == "count": self._write_progress_information_filling_space()
num_tests = self._session.testscollected
progress_length = len(f" [{num_tests}/{num_tests}]")
else:
progress_length = len(" [100%]")
self._progress_nodeids_reported.add(nodeid) return result
if self._is_last_item:
self._write_progress_information_filling_space()
else:
main_color, _ = self._get_main_color()
w = self._width_of_current_line
past_edge = w + progress_length + 1 >= self._screen_width
if past_edge:
msg = self._get_progress_information_message()
self._tw.write(msg + "\n", **{main_color: True})
def _get_progress_information_message(self) -> str: def _get_progress_information_message(self) -> str:
assert self._session assert self._session
collected = self._session.testscollected collected = self._session.testscollected
if self._show_progress_info == "count": if self._show_progress_info == "count":
if collected: if collected:
progress = self._progress_nodeids_reported progress = len(self._progress_nodeids_reported)
counter_format = f"{{:{len(str(collected))}d}}" counter_format = f"{{:{len(str(collected))}d}}"
format_string = f" [{counter_format}/{{}}]" format_string = f" [{counter_format}/{{}}]"
return format_string.format(len(progress), collected) return format_string.format(progress, collected)
return f" [ {collected} / {collected} ]" return f" [ {collected} / {collected} ]"
else: else:
if collected: if collected:
@ -698,6 +697,20 @@ class TerminalReporter:
) )
return " [100%]" return " [100%]"
def _write_progress_information_if_past_edge(self) -> None:
w = self._width_of_current_line
if self._show_progress_info == "count":
assert self._session
num_tests = self._session.testscollected
progress_length = len(f" [{num_tests}/{num_tests}]")
else:
progress_length = len(" [100%]")
past_edge = w + progress_length + 1 >= self._screen_width
if past_edge:
main_color, _ = self._get_main_color()
msg = self._get_progress_information_message()
self._tw.write(msg + "\n", **{main_color: True})
def _write_progress_information_filling_space(self) -> None: def _write_progress_information_filling_space(self) -> None:
color, _ = self._get_main_color() color, _ = self._get_main_color()
msg = self._get_progress_information_message() msg = self._get_progress_information_message()
@ -946,7 +959,7 @@ class TerminalReporter:
line += "[".join(values) line += "[".join(values)
return line return line
# collect_fspath comes from testid which has a "/"-normalized path. # fspath comes from testid which has a "/"-normalized path.
if fspath: if fspath:
res = mkrel(nodeid) res = mkrel(nodeid)
if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace( if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace(