Merge pull request #4299 from RonnyPfannschmidt/callinfo-sanity
refactor callinfo to simplify ctor magic
This commit is contained in:
commit
e0ba1cbf8d
|
@ -23,7 +23,9 @@ def get_skip_exceptions():
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()):
|
if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()):
|
||||||
# let's substitute the excinfo with a pytest.skip one
|
# let's substitute the excinfo with a pytest.skip one
|
||||||
call2 = call.__class__(lambda: runner.skip(str(call.excinfo.value)), call.when)
|
call2 = runner.CallInfo.from_call(
|
||||||
|
lambda: runner.skip(str(call.excinfo.value)), call.when
|
||||||
|
)
|
||||||
call.excinfo = call2.excinfo
|
call.excinfo = call2.excinfo
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
|
import attr
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from .reports import CollectErrorRepr
|
from .reports import CollectErrorRepr
|
||||||
|
@ -189,43 +190,57 @@ def check_interactive_exception(call, report):
|
||||||
def call_runtest_hook(item, when, **kwds):
|
def call_runtest_hook(item, when, **kwds):
|
||||||
hookname = "pytest_runtest_" + when
|
hookname = "pytest_runtest_" + when
|
||||||
ihook = getattr(item.ihook, hookname)
|
ihook = getattr(item.ihook, hookname)
|
||||||
return CallInfo(
|
return CallInfo.from_call(
|
||||||
lambda: ihook(item=item, **kwds),
|
lambda: ihook(item=item, **kwds),
|
||||||
when=when,
|
when=when,
|
||||||
treat_keyboard_interrupt_as_exception=item.config.getvalue("usepdb"),
|
reraise=KeyboardInterrupt if not item.config.getvalue("usepdb") else (),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(repr=False)
|
||||||
class CallInfo(object):
|
class CallInfo(object):
|
||||||
""" Result/Exception info a function invocation. """
|
""" Result/Exception info a function invocation. """
|
||||||
|
|
||||||
#: None or ExceptionInfo object.
|
_result = attr.ib()
|
||||||
excinfo = None
|
# type: Optional[ExceptionInfo]
|
||||||
|
excinfo = attr.ib()
|
||||||
|
start = attr.ib()
|
||||||
|
stop = attr.ib()
|
||||||
|
when = attr.ib()
|
||||||
|
|
||||||
def __init__(self, func, when, treat_keyboard_interrupt_as_exception=False):
|
@property
|
||||||
|
def result(self):
|
||||||
|
if self.excinfo is not None:
|
||||||
|
raise AttributeError("{!r} has no valid result".format(self))
|
||||||
|
return self._result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_call(cls, func, when, reraise=None):
|
||||||
#: context of invocation: one of "setup", "call",
|
#: context of invocation: one of "setup", "call",
|
||||||
#: "teardown", "memocollect"
|
#: "teardown", "memocollect"
|
||||||
self.when = when
|
start = time()
|
||||||
self.start = time()
|
excinfo = None
|
||||||
try:
|
try:
|
||||||
self.result = func()
|
result = func()
|
||||||
except KeyboardInterrupt:
|
|
||||||
if treat_keyboard_interrupt_as_exception:
|
|
||||||
self.excinfo = ExceptionInfo.from_current()
|
|
||||||
else:
|
|
||||||
self.stop = time()
|
|
||||||
raise
|
|
||||||
except: # noqa
|
except: # noqa
|
||||||
self.excinfo = ExceptionInfo.from_current()
|
excinfo = ExceptionInfo.from_current()
|
||||||
self.stop = time()
|
if reraise is not None and excinfo.errisinstance(reraise):
|
||||||
|
raise
|
||||||
|
result = None
|
||||||
|
stop = time()
|
||||||
|
return cls(start=start, stop=stop, when=when, result=result, excinfo=excinfo)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.excinfo:
|
if self.excinfo is not None:
|
||||||
status = "exception: %s" % str(self.excinfo.value)
|
status = "exception"
|
||||||
|
value = self.excinfo.value
|
||||||
else:
|
else:
|
||||||
result = getattr(self, "result", "<NOTSET>")
|
# TODO: investigate unification
|
||||||
status = "result: %r" % (result,)
|
value = repr(self._result)
|
||||||
return "<CallInfo when=%r %s>" % (self.when, status)
|
status = "result"
|
||||||
|
return "<CallInfo when={when!r} {status}: {value}>".format(
|
||||||
|
when=self.when, value=value, status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
|
@ -269,7 +284,7 @@ def pytest_runtest_makereport(item, call):
|
||||||
|
|
||||||
|
|
||||||
def pytest_make_collect_report(collector):
|
def pytest_make_collect_report(collector):
|
||||||
call = CallInfo(lambda: list(collector.collect()), "collect")
|
call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
|
||||||
longrepr = None
|
longrepr = None
|
||||||
if not call.excinfo:
|
if not call.excinfo:
|
||||||
outcome = "passed"
|
outcome = "passed"
|
||||||
|
|
|
@ -487,13 +487,13 @@ def test_report_extra_parameters(reporttype):
|
||||||
|
|
||||||
|
|
||||||
def test_callinfo():
|
def test_callinfo():
|
||||||
ci = runner.CallInfo(lambda: 0, "123")
|
ci = runner.CallInfo.from_call(lambda: 0, "123")
|
||||||
assert ci.when == "123"
|
assert ci.when == "123"
|
||||||
assert ci.result == 0
|
assert ci.result == 0
|
||||||
assert "result" in repr(ci)
|
assert "result" in repr(ci)
|
||||||
assert repr(ci) == "<CallInfo when='123' result: 0>"
|
assert repr(ci) == "<CallInfo when='123' result: 0>"
|
||||||
|
|
||||||
ci = runner.CallInfo(lambda: 0 / 0, "123")
|
ci = runner.CallInfo.from_call(lambda: 0 / 0, "123")
|
||||||
assert ci.when == "123"
|
assert ci.when == "123"
|
||||||
assert not hasattr(ci, "result")
|
assert not hasattr(ci, "result")
|
||||||
assert repr(ci) == "<CallInfo when='123' exception: division by zero>"
|
assert repr(ci) == "<CallInfo when='123' exception: division by zero>"
|
||||||
|
@ -501,16 +501,6 @@ def test_callinfo():
|
||||||
assert "exc" in repr(ci)
|
assert "exc" in repr(ci)
|
||||||
|
|
||||||
|
|
||||||
def test_callinfo_repr_while_running():
|
|
||||||
def repr_while_running():
|
|
||||||
f = sys._getframe().f_back
|
|
||||||
assert "func" in f.f_locals
|
|
||||||
assert repr(f.f_locals["self"]) == "<CallInfo when='when' result: '<NOTSET>'>"
|
|
||||||
|
|
||||||
ci = runner.CallInfo(repr_while_running, "when")
|
|
||||||
assert repr(ci) == "<CallInfo when='when' result: None>"
|
|
||||||
|
|
||||||
|
|
||||||
# design question: do we want general hooks in python files?
|
# design question: do we want general hooks in python files?
|
||||||
# then something like the following functional tests makes sense
|
# then something like the following functional tests makes sense
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue