Type annotate some more hooks & impls
This commit is contained in:
parent
ef34729541
commit
247c4c0482
|
@ -4,6 +4,7 @@ support for presenting detailed information in failing assertions.
|
||||||
import sys
|
import sys
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from typing import Generator
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from _pytest.assertion import rewrite
|
from _pytest.assertion import rewrite
|
||||||
|
@ -14,6 +15,7 @@ from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
|
from _pytest.nodes import Item
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
|
@ -113,7 +115,7 @@ def pytest_collection(session: "Session") -> None:
|
||||||
|
|
||||||
|
|
||||||
@hookimpl(tryfirst=True, hookwrapper=True)
|
@hookimpl(tryfirst=True, hookwrapper=True)
|
||||||
def pytest_runtest_protocol(item):
|
def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
|
||||||
"""Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks
|
"""Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks
|
||||||
|
|
||||||
The rewrite module will use util._reprcompare if
|
The rewrite module will use util._reprcompare if
|
||||||
|
@ -122,8 +124,7 @@ def pytest_runtest_protocol(item):
|
||||||
comparison for the test.
|
comparison for the test.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def callbinrepr(op, left, right):
|
def callbinrepr(op, left: object, right: object) -> Optional[str]:
|
||||||
# type: (str, object, object) -> Optional[str]
|
|
||||||
"""Call the pytest_assertrepr_compare hook and prepare the result
|
"""Call the pytest_assertrepr_compare hook and prepare the result
|
||||||
|
|
||||||
This uses the first result from the hook and then ensures the
|
This uses the first result from the hook and then ensures the
|
||||||
|
@ -156,7 +157,7 @@ def pytest_runtest_protocol(item):
|
||||||
|
|
||||||
if item.ihook.pytest_assertion_pass.get_hookimpls():
|
if item.ihook.pytest_assertion_pass.get_hookimpls():
|
||||||
|
|
||||||
def call_assertion_pass_hook(lineno, orig, expl):
|
def call_assertion_pass_hook(lineno: int, orig: str, expl: str) -> None:
|
||||||
item.ihook.pytest_assertion_pass(
|
item.ihook.pytest_assertion_pass(
|
||||||
item=item, lineno=lineno, orig=orig, expl=expl
|
item=item, lineno=lineno, orig=orig, expl=expl
|
||||||
)
|
)
|
||||||
|
|
|
@ -444,14 +444,12 @@ def _call_reprcompare(ops, results, expls, each_obj):
|
||||||
return expl
|
return expl
|
||||||
|
|
||||||
|
|
||||||
def _call_assertion_pass(lineno, orig, expl):
|
def _call_assertion_pass(lineno: int, orig: str, expl: str) -> None:
|
||||||
# type: (int, str, str) -> None
|
|
||||||
if util._assertion_pass is not None:
|
if util._assertion_pass is not None:
|
||||||
util._assertion_pass(lineno, orig, expl)
|
util._assertion_pass(lineno, orig, expl)
|
||||||
|
|
||||||
|
|
||||||
def _check_if_assertion_pass_impl():
|
def _check_if_assertion_pass_impl() -> bool:
|
||||||
# type: () -> bool
|
|
||||||
"""Checks if any plugins implement the pytest_assertion_pass hook
|
"""Checks if any plugins implement the pytest_assertion_pass hook
|
||||||
in order not to generate explanation unecessarily (might be expensive)"""
|
in order not to generate explanation unecessarily (might be expensive)"""
|
||||||
return True if util._assertion_pass else False
|
return True if util._assertion_pass else False
|
||||||
|
|
|
@ -29,6 +29,7 @@ from _pytest.config import ExitCode
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
from _pytest.python import Module
|
from _pytest.python import Module
|
||||||
|
from _pytest.reports import TestReport
|
||||||
|
|
||||||
README_CONTENT = """\
|
README_CONTENT = """\
|
||||||
# pytest cache directory #
|
# pytest cache directory #
|
||||||
|
@ -265,7 +266,7 @@ class LFPlugin:
|
||||||
if self.active and self.config.getoption("verbose") >= 0:
|
if self.active and self.config.getoption("verbose") >= 0:
|
||||||
return "run-last-failure: %s" % self._report_status
|
return "run-last-failure: %s" % self._report_status
|
||||||
|
|
||||||
def pytest_runtest_logreport(self, report):
|
def pytest_runtest_logreport(self, report: TestReport) -> None:
|
||||||
if (report.when == "call" and report.passed) or report.skipped:
|
if (report.when == "call" and report.passed) or report.skipped:
|
||||||
self.lastfailed.pop(report.nodeid, None)
|
self.lastfailed.pop(report.nodeid, None)
|
||||||
elif report.failed:
|
elif report.failed:
|
||||||
|
|
|
@ -17,6 +17,9 @@ import pytest
|
||||||
from _pytest.compat import TYPE_CHECKING
|
from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
|
from _pytest.fixtures import SubRequest
|
||||||
|
from _pytest.nodes import Collector
|
||||||
|
from _pytest.nodes import Item
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
@ -710,7 +713,7 @@ class CaptureManager:
|
||||||
# Hooks
|
# Hooks
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_make_collect_report(self, collector):
|
def pytest_make_collect_report(self, collector: Collector):
|
||||||
if isinstance(collector, pytest.File):
|
if isinstance(collector, pytest.File):
|
||||||
self.resume_global_capture()
|
self.resume_global_capture()
|
||||||
outcome = yield
|
outcome = yield
|
||||||
|
@ -725,17 +728,17 @@ class CaptureManager:
|
||||||
yield
|
yield
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_runtest_setup(self, item):
|
def pytest_runtest_setup(self, item: Item) -> Generator[None, None, None]:
|
||||||
with self.item_capture("setup", item):
|
with self.item_capture("setup", item):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_runtest_call(self, item):
|
def pytest_runtest_call(self, item: Item) -> Generator[None, None, None]:
|
||||||
with self.item_capture("call", item):
|
with self.item_capture("call", item):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_runtest_teardown(self, item):
|
def pytest_runtest_teardown(self, item: Item) -> Generator[None, None, None]:
|
||||||
with self.item_capture("teardown", item):
|
with self.item_capture("teardown", item):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,18 @@ import functools
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from _pytest import outcomes
|
from _pytest import outcomes
|
||||||
|
from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import ConftestImportFailure
|
from _pytest.config import ConftestImportFailure
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.config import PytestPluginManager
|
from _pytest.config import PytestPluginManager
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.config.exceptions import UsageError
|
from _pytest.config.exceptions import UsageError
|
||||||
|
from _pytest.nodes import Node
|
||||||
|
from _pytest.reports import BaseReport
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from _pytest.runner import CallInfo
|
||||||
|
|
||||||
|
|
||||||
def _validate_usepdb_cls(value):
|
def _validate_usepdb_cls(value):
|
||||||
|
@ -259,7 +265,9 @@ class pytestPDB:
|
||||||
|
|
||||||
|
|
||||||
class PdbInvoke:
|
class PdbInvoke:
|
||||||
def pytest_exception_interact(self, node, call, report):
|
def pytest_exception_interact(
|
||||||
|
self, node: Node, call: "CallInfo", report: BaseReport
|
||||||
|
) -> None:
|
||||||
capman = node.config.pluginmanager.getplugin("capturemanager")
|
capman = node.config.pluginmanager.getplugin("capturemanager")
|
||||||
if capman:
|
if capman:
|
||||||
capman.suspend_global_capture(in_=True)
|
capman.suspend_global_capture(in_=True)
|
||||||
|
@ -306,7 +314,7 @@ def maybe_wrap_pytest_function_for_tracing(pyfuncitem):
|
||||||
wrap_pytest_function_for_tracing(pyfuncitem)
|
wrap_pytest_function_for_tracing(pyfuncitem)
|
||||||
|
|
||||||
|
|
||||||
def _enter_pdb(node, excinfo, rep):
|
def _enter_pdb(node: Node, excinfo, rep: BaseReport) -> BaseReport:
|
||||||
# XXX we re-use the TerminalReporter's terminalwriter
|
# XXX we re-use the TerminalReporter's terminalwriter
|
||||||
# because this seems to avoid some encoding related troubles
|
# because this seems to avoid some encoding related troubles
|
||||||
# for not completely clear reasons.
|
# for not completely clear reasons.
|
||||||
|
@ -330,7 +338,7 @@ def _enter_pdb(node, excinfo, rep):
|
||||||
rep.toterminal(tw)
|
rep.toterminal(tw)
|
||||||
tw.sep(">", "entering PDB")
|
tw.sep(">", "entering PDB")
|
||||||
tb = _postmortem_traceback(excinfo)
|
tb = _postmortem_traceback(excinfo)
|
||||||
rep._pdbshown = True
|
rep._pdbshown = True # type: ignore[attr-defined] # noqa: F821
|
||||||
post_mortem(tb)
|
post_mortem(tb)
|
||||||
return rep
|
return rep
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from typing import Generator
|
||||||
from typing import TextIO
|
from typing import TextIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
|
from _pytest.nodes import Item
|
||||||
from _pytest.store import StoreKey
|
from _pytest.store import StoreKey
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +84,7 @@ class FaultHandlerHooks:
|
||||||
return float(config.getini("faulthandler_timeout") or 0.0)
|
return float(config.getini("faulthandler_timeout") or 0.0)
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True, trylast=True)
|
@pytest.hookimpl(hookwrapper=True, trylast=True)
|
||||||
def pytest_runtest_protocol(self, item):
|
def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]:
|
||||||
timeout = self.get_timeout_config_value(item.config)
|
timeout = self.get_timeout_config_value(item.config)
|
||||||
stderr = item.config._store[fault_handler_stderr_key]
|
stderr = item.config._store[fault_handler_stderr_key]
|
||||||
if timeout > 0 and stderr is not None:
|
if timeout > 0 and stderr is not None:
|
||||||
|
@ -105,7 +107,7 @@ class FaultHandlerHooks:
|
||||||
faulthandler.cancel_dump_traceback_later()
|
faulthandler.cancel_dump_traceback_later()
|
||||||
|
|
||||||
@pytest.hookimpl(tryfirst=True)
|
@pytest.hookimpl(tryfirst=True)
|
||||||
def pytest_exception_interact(self):
|
def pytest_exception_interact(self) -> None:
|
||||||
"""Cancel any traceback dumping due to an interactive exception being
|
"""Cancel any traceback dumping due to an interactive exception being
|
||||||
raised.
|
raised.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -25,10 +25,16 @@ if TYPE_CHECKING:
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
from _pytest.nodes import Collector
|
from _pytest.nodes import Collector
|
||||||
from _pytest.nodes import Item
|
from _pytest.nodes import Item
|
||||||
|
from _pytest.nodes import Node
|
||||||
|
from _pytest.python import Function
|
||||||
from _pytest.python import Metafunc
|
from _pytest.python import Metafunc
|
||||||
from _pytest.python import Module
|
from _pytest.python import Module
|
||||||
from _pytest.python import PyCollector
|
from _pytest.python import PyCollector
|
||||||
from _pytest.reports import BaseReport
|
from _pytest.reports import BaseReport
|
||||||
|
from _pytest.reports import CollectReport
|
||||||
|
from _pytest.reports import TestReport
|
||||||
|
from _pytest.runner import CallInfo
|
||||||
|
from _pytest.terminal import TerminalReporter
|
||||||
|
|
||||||
|
|
||||||
hookspec = HookspecMarker("pytest")
|
hookspec = HookspecMarker("pytest")
|
||||||
|
@ -268,7 +274,7 @@ def pytest_collect_file(path: py.path.local, parent) -> "Optional[Collector]":
|
||||||
# logging hooks for collection
|
# logging hooks for collection
|
||||||
|
|
||||||
|
|
||||||
def pytest_collectstart(collector):
|
def pytest_collectstart(collector: "Collector") -> None:
|
||||||
""" collector starts collecting. """
|
""" collector starts collecting. """
|
||||||
|
|
||||||
|
|
||||||
|
@ -285,7 +291,7 @@ def pytest_deselected(items):
|
||||||
|
|
||||||
|
|
||||||
@hookspec(firstresult=True)
|
@hookspec(firstresult=True)
|
||||||
def pytest_make_collect_report(collector):
|
def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectReport]":
|
||||||
""" perform ``collector.collect()`` and return a CollectReport.
|
""" perform ``collector.collect()`` and return a CollectReport.
|
||||||
|
|
||||||
Stops at first non-None result, see :ref:`firstresult` """
|
Stops at first non-None result, see :ref:`firstresult` """
|
||||||
|
@ -319,7 +325,7 @@ def pytest_pycollect_makeitem(
|
||||||
|
|
||||||
|
|
||||||
@hookspec(firstresult=True)
|
@hookspec(firstresult=True)
|
||||||
def pytest_pyfunc_call(pyfuncitem):
|
def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]:
|
||||||
""" call underlying test function.
|
""" call underlying test function.
|
||||||
|
|
||||||
Stops at first non-None result, see :ref:`firstresult` """
|
Stops at first non-None result, see :ref:`firstresult` """
|
||||||
|
@ -330,7 +336,9 @@ def pytest_generate_tests(metafunc: "Metafunc") -> None:
|
||||||
|
|
||||||
|
|
||||||
@hookspec(firstresult=True)
|
@hookspec(firstresult=True)
|
||||||
def pytest_make_parametrize_id(config: "Config", val, argname) -> Optional[str]:
|
def pytest_make_parametrize_id(
|
||||||
|
config: "Config", val: object, argname: str
|
||||||
|
) -> Optional[str]:
|
||||||
"""Return a user-friendly string representation of the given ``val`` that will be used
|
"""Return a user-friendly string representation of the given ``val`` that will be used
|
||||||
by @pytest.mark.parametrize calls. Return None if the hook doesn't know about ``val``.
|
by @pytest.mark.parametrize calls. Return None if the hook doesn't know about ``val``.
|
||||||
The parameter name is available as ``argname``, if required.
|
The parameter name is available as ``argname``, if required.
|
||||||
|
@ -349,7 +357,7 @@ def pytest_make_parametrize_id(config: "Config", val, argname) -> Optional[str]:
|
||||||
|
|
||||||
|
|
||||||
@hookspec(firstresult=True)
|
@hookspec(firstresult=True)
|
||||||
def pytest_runtestloop(session: "Session"):
|
def pytest_runtestloop(session: "Session") -> Optional[object]:
|
||||||
""" called for performing the main runtest loop
|
""" called for performing the main runtest loop
|
||||||
(after collection finished).
|
(after collection finished).
|
||||||
|
|
||||||
|
@ -360,7 +368,9 @@ def pytest_runtestloop(session: "Session"):
|
||||||
|
|
||||||
|
|
||||||
@hookspec(firstresult=True)
|
@hookspec(firstresult=True)
|
||||||
def pytest_runtest_protocol(item, nextitem):
|
def pytest_runtest_protocol(
|
||||||
|
item: "Item", nextitem: "Optional[Item]"
|
||||||
|
) -> Optional[object]:
|
||||||
""" implements the runtest_setup/call/teardown protocol for
|
""" implements the runtest_setup/call/teardown protocol for
|
||||||
the given test item, including capturing exceptions and calling
|
the given test item, including capturing exceptions and calling
|
||||||
reporting hooks.
|
reporting hooks.
|
||||||
|
@ -399,15 +409,15 @@ def pytest_runtest_logfinish(nodeid, location):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item: "Item") -> None:
|
||||||
""" called before ``pytest_runtest_call(item)``. """
|
""" called before ``pytest_runtest_call(item)``. """
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_call(item):
|
def pytest_runtest_call(item: "Item") -> None:
|
||||||
""" called to execute the test ``item``. """
|
""" called to execute the test ``item``. """
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_teardown(item, nextitem):
|
def pytest_runtest_teardown(item: "Item", nextitem: "Optional[Item]") -> None:
|
||||||
""" called after ``pytest_runtest_call``.
|
""" called after ``pytest_runtest_call``.
|
||||||
|
|
||||||
:arg nextitem: the scheduled-to-be-next test item (None if no further
|
:arg nextitem: the scheduled-to-be-next test item (None if no further
|
||||||
|
@ -418,7 +428,7 @@ def pytest_runtest_teardown(item, nextitem):
|
||||||
|
|
||||||
|
|
||||||
@hookspec(firstresult=True)
|
@hookspec(firstresult=True)
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item: "Item", call: "CallInfo") -> Optional[object]:
|
||||||
""" return a :py:class:`_pytest.runner.TestReport` object
|
""" return a :py:class:`_pytest.runner.TestReport` object
|
||||||
for the given :py:class:`pytest.Item <_pytest.main.Item>` and
|
for the given :py:class:`pytest.Item <_pytest.main.Item>` and
|
||||||
:py:class:`_pytest.runner.CallInfo`.
|
:py:class:`_pytest.runner.CallInfo`.
|
||||||
|
@ -426,7 +436,7 @@ def pytest_runtest_makereport(item, call):
|
||||||
Stops at first non-None result, see :ref:`firstresult` """
|
Stops at first non-None result, see :ref:`firstresult` """
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_logreport(report):
|
def pytest_runtest_logreport(report: "TestReport") -> None:
|
||||||
""" process a test setup/call/teardown report relating to
|
""" process a test setup/call/teardown report relating to
|
||||||
the respective phase of executing a test. """
|
the respective phase of executing a test. """
|
||||||
|
|
||||||
|
@ -511,7 +521,9 @@ def pytest_unconfigure(config: "Config") -> None:
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def pytest_assertrepr_compare(config: "Config", op, left, right):
|
def pytest_assertrepr_compare(
|
||||||
|
config: "Config", op: str, left: object, right: object
|
||||||
|
) -> Optional[List[str]]:
|
||||||
"""return explanation for comparisons in failing assert expressions.
|
"""return explanation for comparisons in failing assert expressions.
|
||||||
|
|
||||||
Return None for no custom explanation, otherwise return a list
|
Return None for no custom explanation, otherwise return a list
|
||||||
|
@ -523,7 +535,7 @@ def pytest_assertrepr_compare(config: "Config", op, left, right):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def pytest_assertion_pass(item, lineno, orig, expl):
|
def pytest_assertion_pass(item, lineno: int, orig: str, expl: str) -> None:
|
||||||
"""
|
"""
|
||||||
**(Experimental)**
|
**(Experimental)**
|
||||||
|
|
||||||
|
@ -637,7 +649,9 @@ def pytest_report_teststatus(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def pytest_terminal_summary(terminalreporter, exitstatus, config: "Config"):
|
def pytest_terminal_summary(
|
||||||
|
terminalreporter: "TerminalReporter", exitstatus: "ExitCode", config: "Config",
|
||||||
|
) -> None:
|
||||||
"""Add a section to terminal summary reporting.
|
"""Add a section to terminal summary reporting.
|
||||||
|
|
||||||
:param _pytest.terminal.TerminalReporter terminalreporter: the internal terminal reporter object
|
:param _pytest.terminal.TerminalReporter terminalreporter: the internal terminal reporter object
|
||||||
|
@ -741,7 +755,9 @@ def pytest_keyboard_interrupt(excinfo):
|
||||||
""" called for keyboard interrupt. """
|
""" called for keyboard interrupt. """
|
||||||
|
|
||||||
|
|
||||||
def pytest_exception_interact(node, call, report):
|
def pytest_exception_interact(
|
||||||
|
node: "Node", call: "CallInfo", report: "BaseReport"
|
||||||
|
) -> None:
|
||||||
"""called when an exception was raised which can potentially be
|
"""called when an exception was raised which can potentially be
|
||||||
interactively handled.
|
interactively handled.
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,9 @@ from _pytest import timing
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import filename_arg
|
from _pytest.config import filename_arg
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
|
from _pytest.reports import TestReport
|
||||||
from _pytest.store import StoreKey
|
from _pytest.store import StoreKey
|
||||||
|
from _pytest.terminal import TerminalReporter
|
||||||
from _pytest.warnings import _issue_warning_captured
|
from _pytest.warnings import _issue_warning_captured
|
||||||
|
|
||||||
|
|
||||||
|
@ -517,7 +519,7 @@ class LogXML:
|
||||||
reporter.record_testreport(report)
|
reporter.record_testreport(report)
|
||||||
return reporter
|
return reporter
|
||||||
|
|
||||||
def pytest_runtest_logreport(self, report):
|
def pytest_runtest_logreport(self, report: TestReport) -> None:
|
||||||
"""handle a setup/call/teardown report, generating the appropriate
|
"""handle a setup/call/teardown report, generating the appropriate
|
||||||
xml tags as necessary.
|
xml tags as necessary.
|
||||||
|
|
||||||
|
@ -661,7 +663,7 @@ class LogXML:
|
||||||
logfile.write(Junit.testsuites([suite_node]).unicode(indent=0))
|
logfile.write(Junit.testsuites([suite_node]).unicode(indent=0))
|
||||||
logfile.close()
|
logfile.close()
|
||||||
|
|
||||||
def pytest_terminal_summary(self, terminalreporter):
|
def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None:
|
||||||
terminalreporter.write_sep("-", "generated xml file: {}".format(self.logfile))
|
terminalreporter.write_sep("-", "generated xml file: {}".format(self.logfile))
|
||||||
|
|
||||||
def add_global_property(self, name, value):
|
def add_global_property(self, name, value):
|
||||||
|
|
|
@ -20,6 +20,7 @@ from _pytest.config import _strtobool
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import create_terminal_writer
|
from _pytest.config import create_terminal_writer
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
|
from _pytest.main import Session
|
||||||
from _pytest.pathlib import Path
|
from _pytest.pathlib import Path
|
||||||
from _pytest.store import StoreKey
|
from _pytest.store import StoreKey
|
||||||
|
|
||||||
|
@ -618,7 +619,7 @@ class LoggingPlugin:
|
||||||
yield
|
yield
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_runtestloop(self, session):
|
def pytest_runtestloop(self, session: Session) -> Generator[None, None, None]:
|
||||||
"""Runs all collected test items."""
|
"""Runs all collected test items."""
|
||||||
|
|
||||||
if session.config.option.collectonly:
|
if session.config.option.collectonly:
|
||||||
|
@ -655,20 +656,21 @@ class LoggingPlugin:
|
||||||
item.add_report_section(when, "log", log)
|
item.add_report_section(when, "log", log)
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_runtest_setup(self, item):
|
def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None, None, None]:
|
||||||
self.log_cli_handler.set_when("setup")
|
self.log_cli_handler.set_when("setup")
|
||||||
|
|
||||||
item._store[catch_log_records_key] = {}
|
empty = {} # type: Dict[str, List[logging.LogRecord]]
|
||||||
|
item._store[catch_log_records_key] = empty
|
||||||
yield from self._runtest_for(item, "setup")
|
yield from self._runtest_for(item, "setup")
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_runtest_call(self, item):
|
def pytest_runtest_call(self, item: nodes.Item) -> Generator[None, None, None]:
|
||||||
self.log_cli_handler.set_when("call")
|
self.log_cli_handler.set_when("call")
|
||||||
|
|
||||||
yield from self._runtest_for(item, "call")
|
yield from self._runtest_for(item, "call")
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_runtest_teardown(self, item):
|
def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None, None, None]:
|
||||||
self.log_cli_handler.set_when("teardown")
|
self.log_cli_handler.set_when("teardown")
|
||||||
|
|
||||||
yield from self._runtest_for(item, "teardown")
|
yield from self._runtest_for(item, "teardown")
|
||||||
|
@ -676,7 +678,7 @@ class LoggingPlugin:
|
||||||
del item._store[catch_log_handler_key]
|
del item._store[catch_log_handler_key]
|
||||||
|
|
||||||
@pytest.hookimpl
|
@pytest.hookimpl
|
||||||
def pytest_runtest_logfinish(self):
|
def pytest_runtest_logfinish(self) -> None:
|
||||||
self.log_cli_handler.set_when("finish")
|
self.log_cli_handler.set_when("finish")
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
||||||
|
|
|
@ -31,6 +31,7 @@ from _pytest.config.argparsing import Parser
|
||||||
from _pytest.fixtures import FixtureManager
|
from _pytest.fixtures import FixtureManager
|
||||||
from _pytest.outcomes import exit
|
from _pytest.outcomes import exit
|
||||||
from _pytest.reports import CollectReport
|
from _pytest.reports import CollectReport
|
||||||
|
from _pytest.reports import TestReport
|
||||||
from _pytest.runner import collect_one_node
|
from _pytest.runner import collect_one_node
|
||||||
from _pytest.runner import SetupState
|
from _pytest.runner import SetupState
|
||||||
|
|
||||||
|
@ -441,7 +442,7 @@ class Session(nodes.FSCollector):
|
||||||
raise self.Interrupted(self.shouldstop)
|
raise self.Interrupted(self.shouldstop)
|
||||||
|
|
||||||
@hookimpl(tryfirst=True)
|
@hookimpl(tryfirst=True)
|
||||||
def pytest_runtest_logreport(self, report) -> None:
|
def pytest_runtest_logreport(self, report: TestReport) -> None:
|
||||||
if report.failed and not hasattr(report, "wasxfail"):
|
if report.failed and not hasattr(report, "wasxfail"):
|
||||||
self.testsfailed += 1
|
self.testsfailed += 1
|
||||||
maxfail = self.config.getvalue("maxfail")
|
maxfail = self.config.getvalue("maxfail")
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from _pytest import python
|
from _pytest import python
|
||||||
from _pytest import unittest
|
from _pytest import unittest
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
|
from _pytest.nodes import Item
|
||||||
|
|
||||||
|
|
||||||
@hookimpl(trylast=True)
|
@hookimpl(trylast=True)
|
||||||
|
@ -20,7 +21,7 @@ def teardown_nose(item):
|
||||||
call_optional(item.parent.obj, "teardown")
|
call_optional(item.parent.obj, "teardown")
|
||||||
|
|
||||||
|
|
||||||
def is_potential_nosetest(item):
|
def is_potential_nosetest(item: Item) -> bool:
|
||||||
# extra check needed since we do not do nose style setup/teardown
|
# extra check needed since we do not do nose style setup/teardown
|
||||||
# on direct unittest style classes
|
# on direct unittest style classes
|
||||||
return isinstance(item, python.Function) and not isinstance(
|
return isinstance(item, python.Function) and not isinstance(
|
||||||
|
|
|
@ -2,11 +2,14 @@
|
||||||
import tempfile
|
import tempfile
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
|
from _pytest.config import create_terminal_writer
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.store import StoreKey
|
from _pytest.store import StoreKey
|
||||||
|
from _pytest.terminal import TerminalReporter
|
||||||
|
|
||||||
|
|
||||||
pastebinfile_key = StoreKey[IO[bytes]]()
|
pastebinfile_key = StoreKey[IO[bytes]]()
|
||||||
|
@ -63,11 +66,11 @@ def pytest_unconfigure(config: Config) -> None:
|
||||||
tr.write_line("pastebin session-log: %s\n" % pastebinurl)
|
tr.write_line("pastebin session-log: %s\n" % pastebinurl)
|
||||||
|
|
||||||
|
|
||||||
def create_new_paste(contents):
|
def create_new_paste(contents: Union[str, bytes]) -> str:
|
||||||
"""
|
"""
|
||||||
Creates a new paste using bpaste.net service.
|
Creates a new paste using bpaste.net service.
|
||||||
|
|
||||||
:contents: paste contents as utf-8 encoded bytes
|
:contents: paste contents string
|
||||||
:returns: url to the pasted contents or error message
|
:returns: url to the pasted contents or error message
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
@ -79,7 +82,7 @@ def create_new_paste(contents):
|
||||||
try:
|
try:
|
||||||
response = (
|
response = (
|
||||||
urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8")
|
urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8")
|
||||||
)
|
) # type: str
|
||||||
except OSError as exc_info: # urllib errors
|
except OSError as exc_info: # urllib errors
|
||||||
return "bad response: %s" % exc_info
|
return "bad response: %s" % exc_info
|
||||||
m = re.search(r'href="/raw/(\w+)"', response)
|
m = re.search(r'href="/raw/(\w+)"', response)
|
||||||
|
@ -89,23 +92,20 @@ def create_new_paste(contents):
|
||||||
return "bad response: invalid format ('" + response + "')"
|
return "bad response: invalid format ('" + response + "')"
|
||||||
|
|
||||||
|
|
||||||
def pytest_terminal_summary(terminalreporter):
|
def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None:
|
||||||
import _pytest.config
|
|
||||||
|
|
||||||
if terminalreporter.config.option.pastebin != "failed":
|
if terminalreporter.config.option.pastebin != "failed":
|
||||||
return
|
return
|
||||||
tr = terminalreporter
|
if "failed" in terminalreporter.stats:
|
||||||
if "failed" in tr.stats:
|
|
||||||
terminalreporter.write_sep("=", "Sending information to Paste Service")
|
terminalreporter.write_sep("=", "Sending information to Paste Service")
|
||||||
for rep in terminalreporter.stats.get("failed"):
|
for rep in terminalreporter.stats["failed"]:
|
||||||
try:
|
try:
|
||||||
msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc
|
msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
msg = tr._getfailureheadline(rep)
|
msg = terminalreporter._getfailureheadline(rep)
|
||||||
file = StringIO()
|
file = StringIO()
|
||||||
tw = _pytest.config.create_terminal_writer(terminalreporter.config, file)
|
tw = create_terminal_writer(terminalreporter.config, file)
|
||||||
rep.toterminal(tw)
|
rep.toterminal(tw)
|
||||||
s = file.getvalue()
|
s = file.getvalue()
|
||||||
assert len(s)
|
assert len(s)
|
||||||
pastebinurl = create_new_paste(s)
|
pastebinurl = create_new_paste(s)
|
||||||
tr.write_line("{} --> {}".format(msg, pastebinurl))
|
terminalreporter.write_line("{} --> {}".format(msg, pastebinurl))
|
||||||
|
|
|
@ -12,6 +12,7 @@ from fnmatch import fnmatch
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
from typing import Generator
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
@ -138,7 +139,7 @@ class LsofFdLeakChecker:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
||||||
def pytest_runtest_protocol(self, item):
|
def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]:
|
||||||
lines1 = self.get_open_files()
|
lines1 = self.get_open_files()
|
||||||
yield
|
yield
|
||||||
if hasattr(sys, "pypy_version_info"):
|
if hasattr(sys, "pypy_version_info"):
|
||||||
|
|
|
@ -173,7 +173,7 @@ def async_warn_and_skip(nodeid: str) -> None:
|
||||||
|
|
||||||
|
|
||||||
@hookimpl(trylast=True)
|
@hookimpl(trylast=True)
|
||||||
def pytest_pyfunc_call(pyfuncitem: "Function"):
|
def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]:
|
||||||
testfunction = pyfuncitem.obj
|
testfunction = pyfuncitem.obj
|
||||||
if is_async_function(testfunction):
|
if is_async_function(testfunction):
|
||||||
async_warn_and_skip(pyfuncitem.nodeid)
|
async_warn_and_skip(pyfuncitem.nodeid)
|
||||||
|
|
|
@ -26,6 +26,9 @@ from _pytest.nodes import Item
|
||||||
from _pytest.outcomes import skip
|
from _pytest.outcomes import skip
|
||||||
from _pytest.pathlib import Path
|
from _pytest.pathlib import Path
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from _pytest.runner import CallInfo
|
||||||
|
|
||||||
|
|
||||||
def getslaveinfoline(node):
|
def getslaveinfoline(node):
|
||||||
try:
|
try:
|
||||||
|
@ -42,7 +45,8 @@ def getslaveinfoline(node):
|
||||||
class BaseReport:
|
class BaseReport:
|
||||||
when = None # type: Optional[str]
|
when = None # type: Optional[str]
|
||||||
location = None # type: Optional[Tuple[str, Optional[int], str]]
|
location = None # type: Optional[Tuple[str, Optional[int], str]]
|
||||||
longrepr = None
|
# TODO: Improve this Any.
|
||||||
|
longrepr = None # type: Optional[Any]
|
||||||
sections = [] # type: List[Tuple[str, str]]
|
sections = [] # type: List[Tuple[str, str]]
|
||||||
nodeid = None # type: str
|
nodeid = None # type: str
|
||||||
|
|
||||||
|
@ -270,7 +274,7 @@ class TestReport(BaseReport):
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_item_and_call(cls, item, call) -> "TestReport":
|
def from_item_and_call(cls, item: Item, call: "CallInfo") -> "TestReport":
|
||||||
"""
|
"""
|
||||||
Factory method to create and fill a TestReport with standard item and call info.
|
Factory method to create and fill a TestReport with standard item and call info.
|
||||||
"""
|
"""
|
||||||
|
@ -281,7 +285,8 @@ class TestReport(BaseReport):
|
||||||
sections = []
|
sections = []
|
||||||
if not call.excinfo:
|
if not call.excinfo:
|
||||||
outcome = "passed"
|
outcome = "passed"
|
||||||
longrepr = None
|
# TODO: Improve this Any.
|
||||||
|
longrepr = None # type: Optional[Any]
|
||||||
else:
|
else:
|
||||||
if not isinstance(excinfo, ExceptionInfo):
|
if not isinstance(excinfo, ExceptionInfo):
|
||||||
outcome = "failed"
|
outcome = "failed"
|
||||||
|
|
|
@ -7,6 +7,7 @@ import py
|
||||||
|
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
|
from _pytest.reports import TestReport
|
||||||
from _pytest.store import StoreKey
|
from _pytest.store import StoreKey
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@ class ResultLog:
|
||||||
testpath = report.fspath
|
testpath = report.fspath
|
||||||
self.write_log_entry(testpath, lettercode, longrepr)
|
self.write_log_entry(testpath, lettercode, longrepr)
|
||||||
|
|
||||||
def pytest_runtest_logreport(self, report):
|
def pytest_runtest_logreport(self, report: TestReport) -> None:
|
||||||
if report.when != "call" and report.passed:
|
if report.when != "call" and report.passed:
|
||||||
return
|
return
|
||||||
res = self.config.hook.pytest_report_teststatus(
|
res = self.config.hook.pytest_report_teststatus(
|
||||||
|
@ -80,6 +81,7 @@ class ResultLog:
|
||||||
elif report.passed:
|
elif report.passed:
|
||||||
longrepr = ""
|
longrepr = ""
|
||||||
elif report.skipped:
|
elif report.skipped:
|
||||||
|
assert report.longrepr is not None
|
||||||
longrepr = str(report.longrepr[2])
|
longrepr = str(report.longrepr[2])
|
||||||
else:
|
else:
|
||||||
longrepr = str(report.longrepr)
|
longrepr = str(report.longrepr)
|
||||||
|
|
|
@ -10,6 +10,7 @@ from typing import Tuple
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
|
||||||
|
from .reports import BaseReport
|
||||||
from .reports import CollectErrorRepr
|
from .reports import CollectErrorRepr
|
||||||
from .reports import CollectReport
|
from .reports import CollectReport
|
||||||
from .reports import TestReport
|
from .reports import TestReport
|
||||||
|
@ -19,6 +20,7 @@ from _pytest._code.code import ExceptionInfo
|
||||||
from _pytest.compat import TYPE_CHECKING
|
from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.nodes import Collector
|
from _pytest.nodes import Collector
|
||||||
|
from _pytest.nodes import Item
|
||||||
from _pytest.nodes import Node
|
from _pytest.nodes import Node
|
||||||
from _pytest.outcomes import Exit
|
from _pytest.outcomes import Exit
|
||||||
from _pytest.outcomes import Skipped
|
from _pytest.outcomes import Skipped
|
||||||
|
@ -29,6 +31,7 @@ if TYPE_CHECKING:
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
|
from _pytest.terminal import TerminalReporter
|
||||||
|
|
||||||
#
|
#
|
||||||
# pytest plugin hooks
|
# pytest plugin hooks
|
||||||
|
@ -46,7 +49,7 @@ def pytest_addoption(parser: Parser) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def pytest_terminal_summary(terminalreporter):
|
def pytest_terminal_summary(terminalreporter: "TerminalReporter") -> None:
|
||||||
durations = terminalreporter.config.option.durations
|
durations = terminalreporter.config.option.durations
|
||||||
verbose = terminalreporter.config.getvalue("verbose")
|
verbose = terminalreporter.config.getvalue("verbose")
|
||||||
if durations is None:
|
if durations is None:
|
||||||
|
@ -86,17 +89,19 @@ def pytest_sessionfinish(session: "Session") -> None:
|
||||||
session._setupstate.teardown_all()
|
session._setupstate.teardown_all()
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_protocol(item, nextitem):
|
def pytest_runtest_protocol(item: Item, nextitem: Optional[Item]) -> bool:
|
||||||
item.ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location)
|
item.ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location)
|
||||||
runtestprotocol(item, nextitem=nextitem)
|
runtestprotocol(item, nextitem=nextitem)
|
||||||
item.ihook.pytest_runtest_logfinish(nodeid=item.nodeid, location=item.location)
|
item.ihook.pytest_runtest_logfinish(nodeid=item.nodeid, location=item.location)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def runtestprotocol(item, log=True, nextitem=None):
|
def runtestprotocol(
|
||||||
|
item: Item, log: bool = True, nextitem: Optional[Item] = None
|
||||||
|
) -> List[TestReport]:
|
||||||
hasrequest = hasattr(item, "_request")
|
hasrequest = hasattr(item, "_request")
|
||||||
if hasrequest and not item._request:
|
if hasrequest and not item._request: # type: ignore[attr-defined] # noqa: F821
|
||||||
item._initrequest()
|
item._initrequest() # type: ignore[attr-defined] # noqa: F821
|
||||||
rep = call_and_report(item, "setup", log)
|
rep = call_and_report(item, "setup", log)
|
||||||
reports = [rep]
|
reports = [rep]
|
||||||
if rep.passed:
|
if rep.passed:
|
||||||
|
@ -108,12 +113,12 @@ def runtestprotocol(item, log=True, nextitem=None):
|
||||||
# after all teardown hooks have been called
|
# after all teardown hooks have been called
|
||||||
# want funcargs and request info to go away
|
# want funcargs and request info to go away
|
||||||
if hasrequest:
|
if hasrequest:
|
||||||
item._request = False
|
item._request = False # type: ignore[attr-defined] # noqa: F821
|
||||||
item.funcargs = None
|
item.funcargs = None # type: ignore[attr-defined] # noqa: F821
|
||||||
return reports
|
return reports
|
||||||
|
|
||||||
|
|
||||||
def show_test_item(item):
|
def show_test_item(item: Item) -> None:
|
||||||
"""Show test function, parameters and the fixtures of the test item."""
|
"""Show test function, parameters and the fixtures of the test item."""
|
||||||
tw = item.config.get_terminal_writer()
|
tw = item.config.get_terminal_writer()
|
||||||
tw.line()
|
tw.line()
|
||||||
|
@ -125,12 +130,12 @@ def show_test_item(item):
|
||||||
tw.flush()
|
tw.flush()
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item: Item) -> None:
|
||||||
_update_current_test_var(item, "setup")
|
_update_current_test_var(item, "setup")
|
||||||
item.session._setupstate.prepare(item)
|
item.session._setupstate.prepare(item)
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_call(item):
|
def pytest_runtest_call(item: Item) -> None:
|
||||||
_update_current_test_var(item, "call")
|
_update_current_test_var(item, "call")
|
||||||
try:
|
try:
|
||||||
del sys.last_type
|
del sys.last_type
|
||||||
|
@ -150,13 +155,15 @@ def pytest_runtest_call(item):
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_teardown(item, nextitem):
|
def pytest_runtest_teardown(item: Item, nextitem: Optional[Item]) -> None:
|
||||||
_update_current_test_var(item, "teardown")
|
_update_current_test_var(item, "teardown")
|
||||||
item.session._setupstate.teardown_exact(item, nextitem)
|
item.session._setupstate.teardown_exact(item, nextitem)
|
||||||
_update_current_test_var(item, None)
|
_update_current_test_var(item, None)
|
||||||
|
|
||||||
|
|
||||||
def _update_current_test_var(item, when):
|
def _update_current_test_var(
|
||||||
|
item: Item, when: Optional["Literal['setup', 'call', 'teardown']"]
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Update :envvar:`PYTEST_CURRENT_TEST` to reflect the current item and stage.
|
Update :envvar:`PYTEST_CURRENT_TEST` to reflect the current item and stage.
|
||||||
|
|
||||||
|
@ -188,11 +195,11 @@ def pytest_report_teststatus(report):
|
||||||
|
|
||||||
|
|
||||||
def call_and_report(
|
def call_and_report(
|
||||||
item, when: "Literal['setup', 'call', 'teardown']", log=True, **kwds
|
item: Item, when: "Literal['setup', 'call', 'teardown']", log: bool = True, **kwds
|
||||||
):
|
) -> TestReport:
|
||||||
call = call_runtest_hook(item, when, **kwds)
|
call = call_runtest_hook(item, when, **kwds)
|
||||||
hook = item.ihook
|
hook = item.ihook
|
||||||
report = hook.pytest_runtest_makereport(item=item, call=call)
|
report = hook.pytest_runtest_makereport(item=item, call=call) # type: TestReport
|
||||||
if log:
|
if log:
|
||||||
hook.pytest_runtest_logreport(report=report)
|
hook.pytest_runtest_logreport(report=report)
|
||||||
if check_interactive_exception(call, report):
|
if check_interactive_exception(call, report):
|
||||||
|
@ -200,15 +207,17 @@ def call_and_report(
|
||||||
return report
|
return report
|
||||||
|
|
||||||
|
|
||||||
def check_interactive_exception(call, report):
|
def check_interactive_exception(call: "CallInfo", report: BaseReport) -> bool:
|
||||||
return call.excinfo and not (
|
return call.excinfo is not None and not (
|
||||||
hasattr(report, "wasxfail")
|
hasattr(report, "wasxfail")
|
||||||
or call.excinfo.errisinstance(Skipped)
|
or call.excinfo.errisinstance(Skipped)
|
||||||
or call.excinfo.errisinstance(bdb.BdbQuit)
|
or call.excinfo.errisinstance(bdb.BdbQuit)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def call_runtest_hook(item, when: "Literal['setup', 'call', 'teardown']", **kwds):
|
def call_runtest_hook(
|
||||||
|
item: Item, when: "Literal['setup', 'call', 'teardown']", **kwds
|
||||||
|
) -> "CallInfo":
|
||||||
if when == "setup":
|
if when == "setup":
|
||||||
ihook = item.ihook.pytest_runtest_setup
|
ihook = item.ihook.pytest_runtest_setup
|
||||||
elif when == "call":
|
elif when == "call":
|
||||||
|
@ -278,13 +287,13 @@ class CallInfo:
|
||||||
excinfo=excinfo,
|
excinfo=excinfo,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
if self.excinfo is None:
|
if self.excinfo is None:
|
||||||
return "<CallInfo when={!r} result: {!r}>".format(self.when, self._result)
|
return "<CallInfo when={!r} result: {!r}>".format(self.when, self._result)
|
||||||
return "<CallInfo when={!r} excinfo={!r}>".format(self.when, self.excinfo)
|
return "<CallInfo when={!r} excinfo={!r}>".format(self.when, self.excinfo)
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item: Item, call: CallInfo) -> TestReport:
|
||||||
return TestReport.from_item_and_call(item, call)
|
return TestReport.from_item_and_call(item, call)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,12 @@ from _pytest.config import Config
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.mark.evaluate import MarkEvaluator
|
from _pytest.mark.evaluate import MarkEvaluator
|
||||||
|
from _pytest.nodes import Item
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
from _pytest.outcomes import skip
|
from _pytest.outcomes import skip
|
||||||
from _pytest.outcomes import xfail
|
from _pytest.outcomes import xfail
|
||||||
|
from _pytest.python import Function
|
||||||
|
from _pytest.runner import CallInfo
|
||||||
from _pytest.store import StoreKey
|
from _pytest.store import StoreKey
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ def pytest_configure(config: Config) -> None:
|
||||||
|
|
||||||
|
|
||||||
@hookimpl(tryfirst=True)
|
@hookimpl(tryfirst=True)
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item: Item) -> None:
|
||||||
# Check if skip or skipif are specified as pytest marks
|
# Check if skip or skipif are specified as pytest marks
|
||||||
item._store[skipped_by_mark_key] = False
|
item._store[skipped_by_mark_key] = False
|
||||||
eval_skipif = MarkEvaluator(item, "skipif")
|
eval_skipif = MarkEvaluator(item, "skipif")
|
||||||
|
@ -96,7 +99,7 @@ def pytest_runtest_setup(item):
|
||||||
|
|
||||||
|
|
||||||
@hookimpl(hookwrapper=True)
|
@hookimpl(hookwrapper=True)
|
||||||
def pytest_pyfunc_call(pyfuncitem):
|
def pytest_pyfunc_call(pyfuncitem: Function):
|
||||||
check_xfail_no_run(pyfuncitem)
|
check_xfail_no_run(pyfuncitem)
|
||||||
outcome = yield
|
outcome = yield
|
||||||
passed = outcome.excinfo is None
|
passed = outcome.excinfo is None
|
||||||
|
@ -104,7 +107,7 @@ def pytest_pyfunc_call(pyfuncitem):
|
||||||
check_strict_xfail(pyfuncitem)
|
check_strict_xfail(pyfuncitem)
|
||||||
|
|
||||||
|
|
||||||
def check_xfail_no_run(item):
|
def check_xfail_no_run(item: Item) -> None:
|
||||||
"""check xfail(run=False)"""
|
"""check xfail(run=False)"""
|
||||||
if not item.config.option.runxfail:
|
if not item.config.option.runxfail:
|
||||||
evalxfail = item._store[evalxfail_key]
|
evalxfail = item._store[evalxfail_key]
|
||||||
|
@ -113,7 +116,7 @@ def check_xfail_no_run(item):
|
||||||
xfail("[NOTRUN] " + evalxfail.getexplanation())
|
xfail("[NOTRUN] " + evalxfail.getexplanation())
|
||||||
|
|
||||||
|
|
||||||
def check_strict_xfail(pyfuncitem):
|
def check_strict_xfail(pyfuncitem: Function) -> None:
|
||||||
"""check xfail(strict=True) for the given PASSING test"""
|
"""check xfail(strict=True) for the given PASSING test"""
|
||||||
evalxfail = pyfuncitem._store[evalxfail_key]
|
evalxfail = pyfuncitem._store[evalxfail_key]
|
||||||
if evalxfail.istrue():
|
if evalxfail.istrue():
|
||||||
|
@ -126,7 +129,7 @@ def check_strict_xfail(pyfuncitem):
|
||||||
|
|
||||||
|
|
||||||
@hookimpl(hookwrapper=True)
|
@hookimpl(hookwrapper=True)
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item: Item, call: CallInfo):
|
||||||
outcome = yield
|
outcome = yield
|
||||||
rep = outcome.get_result()
|
rep = outcome.get_result()
|
||||||
evalxfail = item._store.get(evalxfail_key, None)
|
evalxfail = item._store.get(evalxfail_key, None)
|
||||||
|
@ -171,6 +174,7 @@ def pytest_runtest_makereport(item, call):
|
||||||
# the location of where the skip exception was raised within pytest
|
# the location of where the skip exception was raised within pytest
|
||||||
_, _, reason = rep.longrepr
|
_, _, reason = rep.longrepr
|
||||||
filename, line = item.reportinfo()[:2]
|
filename, line = item.reportinfo()[:2]
|
||||||
|
assert line is not None
|
||||||
rep.longrepr = str(filename), line + 1, reason
|
rep.longrepr = str(filename), line + 1, reason
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import pytest
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
|
from _pytest.reports import TestReport
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser: Parser) -> None:
|
def pytest_addoption(parser: Parser) -> None:
|
||||||
|
@ -73,7 +74,7 @@ class StepwisePlugin:
|
||||||
|
|
||||||
config.hook.pytest_deselected(items=already_passed)
|
config.hook.pytest_deselected(items=already_passed)
|
||||||
|
|
||||||
def pytest_runtest_logreport(self, report):
|
def pytest_runtest_logreport(self, report: TestReport) -> None:
|
||||||
if not self.active:
|
if not self.active:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ from functools import partial
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
from typing import Generator
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Mapping
|
from typing import Mapping
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
@ -30,15 +31,19 @@ from _pytest import timing
|
||||||
from _pytest._io import TerminalWriter
|
from _pytest._io import TerminalWriter
|
||||||
from _pytest._io.wcwidth import wcswidth
|
from _pytest._io.wcwidth import wcswidth
|
||||||
from _pytest.compat import order_preserving_dict
|
from _pytest.compat import order_preserving_dict
|
||||||
|
from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.config import _PluggyPlugin
|
from _pytest.config import _PluggyPlugin
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import ExitCode
|
from _pytest.config import ExitCode
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.deprecated import TERMINALWRITER_WRITER
|
from _pytest.deprecated import TERMINALWRITER_WRITER
|
||||||
from _pytest.main import Session
|
|
||||||
from _pytest.reports import CollectReport
|
from _pytest.reports import CollectReport
|
||||||
from _pytest.reports import TestReport
|
from _pytest.reports import TestReport
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from _pytest.main import Session
|
||||||
|
|
||||||
|
|
||||||
REPORT_COLLECTING_RESOLUTION = 0.5
|
REPORT_COLLECTING_RESOLUTION = 0.5
|
||||||
|
|
||||||
KNOWN_TYPES = (
|
KNOWN_TYPES = (
|
||||||
|
@ -610,7 +615,7 @@ class TerminalReporter:
|
||||||
self.write_line(line)
|
self.write_line(line)
|
||||||
|
|
||||||
@pytest.hookimpl(trylast=True)
|
@pytest.hookimpl(trylast=True)
|
||||||
def pytest_sessionstart(self, session: Session) -> None:
|
def pytest_sessionstart(self, session: "Session") -> None:
|
||||||
self._session = session
|
self._session = session
|
||||||
self._sessionstarttime = timing.time()
|
self._sessionstarttime = timing.time()
|
||||||
if not self.showheader:
|
if not self.showheader:
|
||||||
|
@ -720,7 +725,9 @@ class TerminalReporter:
|
||||||
self._tw.line("{}{}".format(indent + " ", line))
|
self._tw.line("{}{}".format(indent + " ", line))
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_sessionfinish(self, session: Session, exitstatus: Union[int, ExitCode]):
|
def pytest_sessionfinish(
|
||||||
|
self, session: "Session", exitstatus: Union[int, ExitCode]
|
||||||
|
):
|
||||||
outcome = yield
|
outcome = yield
|
||||||
outcome.get_result()
|
outcome.get_result()
|
||||||
self._tw.line("")
|
self._tw.line("")
|
||||||
|
@ -745,7 +752,7 @@ class TerminalReporter:
|
||||||
self.summary_stats()
|
self.summary_stats()
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_terminal_summary(self):
|
def pytest_terminal_summary(self) -> Generator[None, None, None]:
|
||||||
self.summary_errors()
|
self.summary_errors()
|
||||||
self.summary_failures()
|
self.summary_failures()
|
||||||
self.summary_warnings()
|
self.summary_warnings()
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
""" discovery and running of std-library "unittest" style tests. """
|
""" discovery and running of std-library "unittest" style tests. """
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
from typing import Any
|
||||||
|
from typing import Generator
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
@ -253,7 +255,7 @@ class TestCaseFunction(Function):
|
||||||
|
|
||||||
|
|
||||||
@hookimpl(tryfirst=True)
|
@hookimpl(tryfirst=True)
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item: Item, call: CallInfo) -> None:
|
||||||
if isinstance(item, TestCaseFunction):
|
if isinstance(item, TestCaseFunction):
|
||||||
if item._excinfo:
|
if item._excinfo:
|
||||||
call.excinfo = item._excinfo.pop(0)
|
call.excinfo = item._excinfo.pop(0)
|
||||||
|
@ -263,7 +265,13 @@ def pytest_runtest_makereport(item, call):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
unittest = sys.modules.get("unittest")
|
unittest = sys.modules.get("unittest")
|
||||||
if unittest and call.excinfo and call.excinfo.errisinstance(unittest.SkipTest):
|
if (
|
||||||
|
unittest
|
||||||
|
and call.excinfo
|
||||||
|
and call.excinfo.errisinstance(
|
||||||
|
unittest.SkipTest # type: ignore[attr-defined] # noqa: F821
|
||||||
|
)
|
||||||
|
):
|
||||||
# let's substitute the excinfo with a pytest.skip one
|
# let's substitute the excinfo with a pytest.skip one
|
||||||
call2 = CallInfo.from_call(
|
call2 = CallInfo.from_call(
|
||||||
lambda: pytest.skip(str(call.excinfo.value)), call.when
|
lambda: pytest.skip(str(call.excinfo.value)), call.when
|
||||||
|
@ -275,9 +283,9 @@ def pytest_runtest_makereport(item, call):
|
||||||
|
|
||||||
|
|
||||||
@hookimpl(hookwrapper=True)
|
@hookimpl(hookwrapper=True)
|
||||||
def pytest_runtest_protocol(item):
|
def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
|
||||||
if isinstance(item, TestCaseFunction) and "twisted.trial.unittest" in sys.modules:
|
if isinstance(item, TestCaseFunction) and "twisted.trial.unittest" in sys.modules:
|
||||||
ut = sys.modules["twisted.python.failure"]
|
ut = sys.modules["twisted.python.failure"] # type: Any
|
||||||
Failure__init__ = ut.Failure.__init__
|
Failure__init__ = ut.Failure.__init__
|
||||||
check_testcase_implements_trial_reporter()
|
check_testcase_implements_trial_reporter()
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
|
from _pytest.nodes import Item
|
||||||
|
from _pytest.terminal import TerminalReporter
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing_extensions import Type
|
from typing_extensions import Type
|
||||||
|
@ -145,7 +147,7 @@ def warning_record_to_str(warning_message):
|
||||||
|
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
||||||
def pytest_runtest_protocol(item):
|
def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
|
||||||
with catch_warnings_for_item(
|
with catch_warnings_for_item(
|
||||||
config=item.config, ihook=item.ihook, when="runtest", item=item
|
config=item.config, ihook=item.ihook, when="runtest", item=item
|
||||||
):
|
):
|
||||||
|
@ -162,7 +164,9 @@ def pytest_collection(session: Session) -> Generator[None, None, None]:
|
||||||
|
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_terminal_summary(terminalreporter):
|
def pytest_terminal_summary(
|
||||||
|
terminalreporter: TerminalReporter,
|
||||||
|
) -> Generator[None, None, None]:
|
||||||
config = terminalreporter.config
|
config = terminalreporter.config
|
||||||
with catch_warnings_for_item(
|
with catch_warnings_for_item(
|
||||||
config=config, ihook=config.hook, when="config", item=None
|
config=config, ihook=config.hook, when="config", item=None
|
||||||
|
|
|
@ -884,7 +884,7 @@ def test_store_except_info_on_error() -> None:
|
||||||
raise IndexError("TEST")
|
raise IndexError("TEST")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
runner.pytest_runtest_call(ItemMightRaise())
|
runner.pytest_runtest_call(ItemMightRaise()) # type: ignore[arg-type] # noqa: F821
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
# Check that exception info is stored on sys
|
# Check that exception info is stored on sys
|
||||||
|
@ -895,7 +895,7 @@ def test_store_except_info_on_error() -> None:
|
||||||
|
|
||||||
# The next run should clear the exception info stored by the previous run
|
# The next run should clear the exception info stored by the previous run
|
||||||
ItemMightRaise.raise_error = False
|
ItemMightRaise.raise_error = False
|
||||||
runner.pytest_runtest_call(ItemMightRaise())
|
runner.pytest_runtest_call(ItemMightRaise()) # type: ignore[arg-type] # noqa: F821
|
||||||
assert not hasattr(sys, "last_type")
|
assert not hasattr(sys, "last_type")
|
||||||
assert not hasattr(sys, "last_value")
|
assert not hasattr(sys, "last_value")
|
||||||
assert not hasattr(sys, "last_traceback")
|
assert not hasattr(sys, "last_traceback")
|
||||||
|
|
Loading…
Reference in New Issue