Add experimental _to_json and _from_json to TestReport and CollectReport
This methods were moved from xdist (ca03269). Our intention is to keep this code closer to the core, given that it might break easily due to refactorings. Having it in the core might also allow to improve the code by moving some responsibility to the "code" objects (ReprEntry, etc) which are often found in the reports. Finally pytest-xdist and pytest-subtests can use those functions instead of coding it themselves.
This commit is contained in:
@@ -1,6 +1,14 @@
|
||||
import py
|
||||
import six
|
||||
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import ReprEntry
|
||||
from _pytest._code.code import ReprEntryNative
|
||||
from _pytest._code.code import ReprExceptionInfo
|
||||
from _pytest._code.code import ReprFileLocation
|
||||
from _pytest._code.code import ReprFuncArgs
|
||||
from _pytest._code.code import ReprLocals
|
||||
from _pytest._code.code import ReprTraceback
|
||||
from _pytest._code.code import TerminalRepr
|
||||
from _pytest.outcomes import skip
|
||||
|
||||
@@ -137,6 +145,130 @@ class BaseReport(object):
|
||||
fspath, lineno, domain = self.location
|
||||
return domain
|
||||
|
||||
def _to_json(self):
|
||||
"""
|
||||
This was originally the serialize_report() function from xdist (ca03269).
|
||||
|
||||
Returns the contents of this report as a dict of builtin entries, suitable for
|
||||
serialization.
|
||||
|
||||
Experimental method.
|
||||
"""
|
||||
|
||||
def disassembled_report(rep):
|
||||
reprtraceback = rep.longrepr.reprtraceback.__dict__.copy()
|
||||
reprcrash = rep.longrepr.reprcrash.__dict__.copy()
|
||||
|
||||
new_entries = []
|
||||
for entry in reprtraceback["reprentries"]:
|
||||
entry_data = {
|
||||
"type": type(entry).__name__,
|
||||
"data": entry.__dict__.copy(),
|
||||
}
|
||||
for key, value in entry_data["data"].items():
|
||||
if hasattr(value, "__dict__"):
|
||||
entry_data["data"][key] = value.__dict__.copy()
|
||||
new_entries.append(entry_data)
|
||||
|
||||
reprtraceback["reprentries"] = new_entries
|
||||
|
||||
return {
|
||||
"reprcrash": reprcrash,
|
||||
"reprtraceback": reprtraceback,
|
||||
"sections": rep.longrepr.sections,
|
||||
}
|
||||
|
||||
d = self.__dict__.copy()
|
||||
if hasattr(self.longrepr, "toterminal"):
|
||||
if hasattr(self.longrepr, "reprtraceback") and hasattr(
|
||||
self.longrepr, "reprcrash"
|
||||
):
|
||||
d["longrepr"] = disassembled_report(self)
|
||||
else:
|
||||
d["longrepr"] = six.text_type(self.longrepr)
|
||||
else:
|
||||
d["longrepr"] = self.longrepr
|
||||
for name in d:
|
||||
if isinstance(d[name], py.path.local):
|
||||
d[name] = str(d[name])
|
||||
elif name == "result":
|
||||
d[name] = None # for now
|
||||
return d
|
||||
|
||||
@classmethod
|
||||
def _from_json(cls, reportdict):
|
||||
"""
|
||||
This was originally the serialize_report() function from xdist (ca03269).
|
||||
|
||||
Factory method that returns either a TestReport or CollectReport, depending on the calling
|
||||
class. It's the callers responsibility to know which class to pass here.
|
||||
|
||||
Experimental method.
|
||||
"""
|
||||
if reportdict["longrepr"]:
|
||||
if (
|
||||
"reprcrash" in reportdict["longrepr"]
|
||||
and "reprtraceback" in reportdict["longrepr"]
|
||||
):
|
||||
|
||||
reprtraceback = reportdict["longrepr"]["reprtraceback"]
|
||||
reprcrash = reportdict["longrepr"]["reprcrash"]
|
||||
|
||||
unserialized_entries = []
|
||||
reprentry = None
|
||||
for entry_data in reprtraceback["reprentries"]:
|
||||
data = entry_data["data"]
|
||||
entry_type = entry_data["type"]
|
||||
if entry_type == "ReprEntry":
|
||||
reprfuncargs = None
|
||||
reprfileloc = None
|
||||
reprlocals = None
|
||||
if data["reprfuncargs"]:
|
||||
reprfuncargs = ReprFuncArgs(**data["reprfuncargs"])
|
||||
if data["reprfileloc"]:
|
||||
reprfileloc = ReprFileLocation(**data["reprfileloc"])
|
||||
if data["reprlocals"]:
|
||||
reprlocals = ReprLocals(data["reprlocals"]["lines"])
|
||||
|
||||
reprentry = ReprEntry(
|
||||
lines=data["lines"],
|
||||
reprfuncargs=reprfuncargs,
|
||||
reprlocals=reprlocals,
|
||||
filelocrepr=reprfileloc,
|
||||
style=data["style"],
|
||||
)
|
||||
elif entry_type == "ReprEntryNative":
|
||||
reprentry = ReprEntryNative(data["lines"])
|
||||
else:
|
||||
_report_unserialization_failure(entry_type, cls, reportdict)
|
||||
unserialized_entries.append(reprentry)
|
||||
reprtraceback["reprentries"] = unserialized_entries
|
||||
|
||||
exception_info = ReprExceptionInfo(
|
||||
reprtraceback=ReprTraceback(**reprtraceback),
|
||||
reprcrash=ReprFileLocation(**reprcrash),
|
||||
)
|
||||
|
||||
for section in reportdict["longrepr"]["sections"]:
|
||||
exception_info.addsection(*section)
|
||||
reportdict["longrepr"] = exception_info
|
||||
|
||||
return cls(**reportdict)
|
||||
|
||||
|
||||
def _report_unserialization_failure(type_name, report_class, reportdict):
|
||||
from pprint import pprint
|
||||
|
||||
url = "https://github.com/pytest-dev/pytest/issues"
|
||||
stream = py.io.TextIO()
|
||||
pprint("-" * 100, stream=stream)
|
||||
pprint("INTERNALERROR: Unknown entry type returned: %s" % type_name, stream=stream)
|
||||
pprint("report_name: %s" % report_class, stream=stream)
|
||||
pprint(reportdict, stream=stream)
|
||||
pprint("Please report this bug at %s" % url, stream=stream)
|
||||
pprint("-" * 100, stream=stream)
|
||||
assert 0, stream.getvalue()
|
||||
|
||||
|
||||
class TestReport(BaseReport):
|
||||
""" Basic test report object (also used for setup and teardown calls if
|
||||
|
||||
Reference in New Issue
Block a user