Ejemplo n.º 1
0
    def _write_entry_lines(self, tw: TerminalWriter) -> None:
        """Write the source code portions of a list of traceback entries with syntax highlighting.

        Usually entries are lines like these:

            "     x = 1"
            ">    assert x == 2"
            "E    assert 1 == 2"

        This function takes care of rendering the "source" portions of it (the lines without
        the "E" prefix) using syntax highlighting, taking care to not highlighting the ">"
        character, as doing so might break line continuations.
        """

        if not self.lines:
            return

        # separate indents and source lines that are not failures: we want to
        # highlight the code but not the indentation, which may contain markers
        # such as ">   assert 0"
        fail_marker = "{}   ".format(FormattedExcinfo.fail_marker)
        indent_size = len(fail_marker)
        indents = []
        source_lines = []
        failure_lines = []
        seeing_failures = False
        for line in self.lines:
            is_source_line = not line.startswith(fail_marker)
            if is_source_line:
                assert not seeing_failures, (
                    "Unexpected failure lines between source lines:\n"
                    + "\n".join(self.lines)
                )
                if self.style == "value":
                    source_lines.append(line)
                else:
                    indents.append(line[:indent_size])
                    source_lines.append(line[indent_size:])
            else:
                seeing_failures = True
                failure_lines.append(line)

        tw._write_source(source_lines, indents)

        # failure lines are always completely red and bold
        for line in failure_lines:
            tw.line(line, bold=True, red=True)
def _get_error_contents_from_report(report):
    if report.longrepr is not None:
        try:
            tw = TerminalWriter(stringio=True)
            stringio = tw.stringio
        except TypeError:
            import io
            stringio = io.StringIO()
            tw = TerminalWriter(file=stringio)
        tw.hasmarkup = False
        report.toterminal(tw)
        exc = stringio.getvalue()
        s = exc.strip()
        if s:
            return s

    return ''
Ejemplo n.º 3
0
    def toterminal(self, out: TerminalWriter) -> None:
        if hasattr(self, "node"):
            out.line(getworkerinfoline(self.node))

        longrepr = self.longrepr
        if longrepr is None:
            return

        if hasattr(longrepr, "toterminal"):
            longrepr_terminal = cast(TerminalRepr, longrepr)
            longrepr_terminal.toterminal(out)
        else:
            try:
                s = str(longrepr)
            except UnicodeEncodeError:
                s = "<unprintable longrepr>"
            out.line(s)
Ejemplo n.º 4
0
def main(args=None, plugins=None) -> Union[int, ExitCode]:
    """ return exit code, after performing an in-process test run.

    :arg args: list of command line arguments.

    :arg plugins: list of plugin objects to be auto-registered during
                  initialization.
    """
    try:
        try:
            config = _prepareconfig(args, plugins)
        except ConftestImportFailure as e:
            exc_info = ExceptionInfo(e.excinfo)
            tw = TerminalWriter(sys.stderr)
            tw.line(
                "ImportError while loading conftest '{e.path}'.".format(e=e), red=True
            )
            exc_info.traceback = exc_info.traceback.filter(
                filter_traceback_for_conftest_import_failure
            )
            exc_repr = (
                exc_info.getrepr(style="short", chain=False)
                if exc_info.traceback
                else exc_info.exconly()
            )
            formatted_tb = str(exc_repr)
            for line in formatted_tb.splitlines():
                tw.line(line.rstrip(), red=True)
            return ExitCode.USAGE_ERROR
        else:
            try:
                ret = config.hook.pytest_cmdline_main(
                    config=config
                )  # type: Union[ExitCode, int]
                try:
                    return ExitCode(ret)
                except ValueError:
                    return ret
            finally:
                config._ensure_unconfigure()
    except UsageError as e:
        tw = TerminalWriter(sys.stderr)
        for msg in e.args:
            tw.line("ERROR: {}\n".format(msg), red=True)
        return ExitCode.USAGE_ERROR
Ejemplo n.º 5
0
    def test_exc_chain_repr_without_traceback(self, importasmod, reason, description):
        """
        Handle representation of exception chains where one of the exceptions doesn't have a
        real traceback, such as those raised in a subprocess submitted by the multiprocessing
        module (#1984).
        """
        exc_handling_code = " from e" if reason == "cause" else ""
        mod = importasmod(
            """
            def f():
                try:
                    g()
                except Exception as e:
                    raise RuntimeError('runtime problem'){exc_handling_code}
            def g():
                raise ValueError('invalid value')
        """.format(
                exc_handling_code=exc_handling_code
            )
        )

        with pytest.raises(RuntimeError) as excinfo:
            mod.f()

        # emulate the issue described in #1984
        attr = "__%s__" % reason
        getattr(excinfo.value, attr).__traceback__ = None

        r = excinfo.getrepr()
        file = io.StringIO()
        tw = TerminalWriter(file=file)
        tw.hasmarkup = False
        r.toterminal(tw)

        matcher = LineMatcher(file.getvalue().splitlines())
        matcher.fnmatch_lines(
            [
                "ValueError: invalid value",
                description,
                "* except Exception as e:",
                "> * raise RuntimeError('runtime problem')" + exc_handling_code,
                "E *RuntimeError: runtime problem",
            ]
        )
Ejemplo n.º 6
0
    def _write_entry_lines(self, tw: TerminalWriter) -> None:
        """Write the source code portions of a list of traceback entries with syntax highlighting.

        Usually entries are lines like these:

            "     x = 1"
            ">    assert x == 2"
            "E    assert 1 == 2"

        This function takes care of rendering the "source" portions of it (the lines without
        the "E" prefix) using syntax highlighting, taking care to not highlighting the ">"
        character, as doing so might break line continuations.
        """

        if not self.lines:
            return

        # separate indents and source lines that are not failures: we want to
        # highlight the code but not the indentation, which may contain markers
        # such as ">   assert 0"
        fail_marker = f"{FormattedExcinfo.fail_marker}   "
        indent_size = len(fail_marker)
        indents: List[str] = []
        source_lines: List[str] = []
        failure_lines: List[str] = []
        for index, line in enumerate(self.lines):
            is_failure_line = line.startswith(fail_marker)
            if is_failure_line:
                # from this point on all lines are considered part of the failure
                failure_lines.extend(self.lines[index:])
                break
            else:
                if self.style == "value":
                    source_lines.append(line)
                else:
                    indents.append(line[:indent_size])
                    source_lines.append(line[indent_size:])

        tw._write_source(source_lines, indents)

        # failure lines are always completely red and bold
        for line in failure_lines:
            tw.line(line, bold=True, red=True)
Ejemplo n.º 7
0
 def test_format_excinfo(self, importasmod, reproptions):
     mod = importasmod("""
         def g(x):
             raise ValueError(x)
         def f():
             g(3)
     """)
     excinfo = pytest.raises(ValueError, mod.f)
     tw = TerminalWriter(stringio=True)
     repr = excinfo.getrepr(**reproptions)
     repr.toterminal(tw)
     assert tw.stringio.getvalue()
Ejemplo n.º 8
0
    def toterminal(self, tw: TerminalWriter) -> None:
        if self.style == "short":
            assert self.reprfileloc is not None
            self.reprfileloc.toterminal(tw)
            self._write_entry_lines(tw)
            if self.reprlocals:
                self.reprlocals.toterminal(tw, indent=" " * 8)
            return

        if self.reprfuncargs:
            self.reprfuncargs.toterminal(tw)

        self._write_entry_lines(tw)

        if self.reprlocals:
            tw.line("")
            self.reprlocals.toterminal(tw)
        if self.reprfileloc:
            if self.lines:
                tw.line("")
            self.reprfileloc.toterminal(tw)
Ejemplo n.º 9
0
    def _write_entry_lines(self, tw: TerminalWriter) -> None:
        """Writes the source code portions of a list of traceback entries with syntax highlighting.

        Usually entries are lines like these:

            "     x = 1"
            ">    assert x == 2"
            "E    assert 1 == 2"

        This function takes care of rendering the "source" portions of it (the lines without
        the "E" prefix) using syntax highlighting, taking care to not highlighting the ">"
        character, as doing so might break line continuations.
        """

        indent_size = 4

        def is_fail(line):
            return line.startswith("{}   ".format(
                FormattedExcinfo.fail_marker))

        if not self.lines:
            return

        # separate indents and source lines that are not failures: we want to
        # highlight the code but not the indentation, which may contain markers
        # such as ">   assert 0"
        indents = []
        source_lines = []
        for line in self.lines:
            if not is_fail(line):
                indents.append(line[:indent_size])
                source_lines.append(line[indent_size:])

        tw._write_source(source_lines, indents)

        # failure lines are always completely red and bold
        for line in (x for x in self.lines if is_fail(x)):
            tw.line(line, bold=True, red=True)
Ejemplo n.º 10
0
def test_colored_short_level() -> None:
    logfmt = "%(levelname).1s %(message)s"

    record = logging.LogRecord(
        name="dummy",
        level=logging.INFO,
        pathname="dummypath",
        lineno=10,
        msg="Test Message",
        args=(),
        exc_info=None,
    )

    class ColorConfig:
        class option:
            pass

    tw = TerminalWriter()
    tw.hasmarkup = True
    formatter = ColoredLevelFormatter(tw, logfmt)
    output = formatter.format(record)
    # the I (of INFO) is colored
    assert output == ("\x1b[32mI\x1b[0m Test Message")
Ejemplo n.º 11
0
def test_coloredlogformatter() -> None:
    logfmt = "%(filename)-25s %(lineno)4d %(levelname)-8s %(message)s"

    record = logging.LogRecord(
        name="dummy",
        level=logging.INFO,
        pathname="dummypath",
        lineno=10,
        msg="Test Message",
        args=(),
        exc_info=None,
    )

    tw = TerminalWriter()
    tw.hasmarkup = True
    formatter = ColoredLevelFormatter(tw, logfmt)
    output = formatter.format(record)
    assert output == (
        "dummypath                   10 \x1b[32mINFO    \x1b[0m Test Message")

    tw.hasmarkup = False
    formatter = ColoredLevelFormatter(tw, logfmt)
    output = formatter.format(record)
    assert output == ("dummypath                   10 INFO     Test Message")
Ejemplo n.º 12
0
    def test_format_excinfo(self, reproptions: Dict[str, Any]) -> None:
        def bar():
            assert False, "some error"

        def foo():
            bar()

        # using inline functions as opposed to importasmod so we get source code lines
        # in the tracebacks (otherwise getinspect doesn't find the source code).
        with pytest.raises(AssertionError) as excinfo:
            foo()
        file = io.StringIO()
        tw = TerminalWriter(file=file)
        repr = excinfo.getrepr(**reproptions)
        repr.toterminal(tw)
        assert file.getvalue()
Ejemplo n.º 13
0
 def toterminal(self, tw: TerminalWriter) -> None:
     if self.style == "short":
         assert self.reprfileloc is not None
         self.reprfileloc.toterminal(tw)
         for line in self.lines:
             red = line.startswith("E   ")
             tw.line(line, bold=True, red=red)
         return
     if self.reprfuncargs:
         self.reprfuncargs.toterminal(tw)
     for line in self.lines:
         red = line.startswith("E   ")
         tw.line(line, bold=True, red=red)
     if self.reprlocals:
         tw.line("")
         self.reprlocals.toterminal(tw)
     if self.reprfileloc:
         if self.lines:
             tw.line("")
         self.reprfileloc.toterminal(tw)
Ejemplo n.º 14
0
    def toterminal(self, tw: TerminalWriter) -> None:
        """
        Write the failure summary to given ``TerminalWriter``.

        Text includes failed step number (starting from ``1``), type of failure (explicit fail(), assertion failure, exception)
        and a traceback formatted in traditional Python style with hidden frames removed.
        """
        exc: StepperException = self.excinfo.value
        tw.line(f"test execution failed at step #{exc.step_n}, reason: {exc.reason}", red=True)

        tb = self.format_traceback()

        tw.line("traceback:")
        tw.line("\n".join(map(lambda x: str(x).strip(), tb)))
Ejemplo n.º 15
0
    def toterminal(self, tw: TerminalWriter) -> None:
        # The entries might have different styles.
        for i, entry in enumerate(self.reprentries):
            if entry.style == "long":
                tw.line("")
            entry.toterminal(tw)
            if i < len(self.reprentries) - 1:
                next_entry = self.reprentries[i + 1]
                if (entry.style == "long" or entry.style == "short"
                        and next_entry.style == "long"):
                    tw.sep(self.entrysep)

        if self.extraline:
            tw.line(self.extraline)
Ejemplo n.º 16
0
    def pytest_exception_interact(self, report, call):
        """
        Called when an exception was raised which can potentially be
        interactively handled.

        With the `--interactive` flag, outputs the full repr of the failed test
        and opens an interactive shell using `brownie._cli.console.Console`.

        Arguments
        ---------
        report : _pytest.reports.BaseReport
            Report object for the failed test.
        call : _pytest.runner.CallInfo
            Result/Exception info for the failed test.
        """
        if self.config.getoption("interactive") and report.failed:
            capman = self.config.pluginmanager.get_plugin("capturemanager")
            if capman:
                capman.suspend_global_capture(in_=True)

            tw = TerminalWriter()
            report.longrepr.toterminal(tw)

            locals_dict = call.excinfo.traceback[-1].locals
            locals_dict = {k: v for k, v in locals_dict.items() if not k.startswith("@")}
            try:
                CONFIG.argv["cli"] = "console"
                shell = Console(self.project, extra_locals={"_callinfo": call, **locals_dict})
                shell.interact(
                    banner=f"\nInteractive mode enabled. Use quit() to continue running tests.",
                    exitmsg="",
                )
            except SystemExit:
                pass
            finally:
                CONFIG.argv["cli"] = "test"

            print("Continuing tests...")
            if capman:
                capman.resume_global_capture()
Ejemplo n.º 17
0
 def toterminal(self, tw: TerminalWriter) -> None:
     if self.args:
         linesofar = ""
         for name, value in self.args:
             ns = f"{name} = {value}"
             if len(ns) + len(linesofar) + 2 > tw.fullwidth:
                 if linesofar:
                     tw.line(linesofar)
                 linesofar = ns
             else:
                 if linesofar:
                     linesofar += ", " + ns
                 else:
                     linesofar = ns
         if linesofar:
             tw.line(linesofar)
         tw.line("")
Ejemplo n.º 18
0
    def __init__(self, terminalwriter: TerminalWriter, *args,
                 **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self._original_fmt = self._style._fmt
        self._level_to_fmt_mapping: Dict[int, str] = {}

        assert self._fmt is not None
        levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt)
        if not levelname_fmt_match:
            return
        levelname_fmt = levelname_fmt_match.group()

        for level, color_opts in self.LOGLEVEL_COLOROPTS.items():
            formatted_levelname = levelname_fmt % {
                "levelname": logging.getLevelName(level)
            }

            # add ANSI escape sequences around the formatted levelname
            color_kwargs = {name: True for name in color_opts}
            colorized_formatted_levelname = terminalwriter.markup(
                formatted_levelname, **color_kwargs)
            self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub(
                colorized_formatted_levelname, self._fmt)
Ejemplo n.º 19
0
def create_terminal_writer(
    config: Config, file: Optional[TextIO] = None
) -> TerminalWriter:
    """Create a TerminalWriter instance configured according to the options
    in the config object.

    Every code which requires a TerminalWriter object and has access to a
    config object should use this function.
    """
    tw = TerminalWriter(file=file)

    if config.option.color == "yes":
        tw.hasmarkup = True
    elif config.option.color == "no":
        tw.hasmarkup = False

    if config.option.code_highlight == "yes":
        tw.code_highlight = True
    elif config.option.code_highlight == "no":
        tw.code_highlight = False

    return tw
Ejemplo n.º 20
0
 def toterminal(self, tw: TerminalWriter, indent="") -> None:
     for line in self.lines:
         tw.line(indent + line)
Ejemplo n.º 21
0
    def dump_json(self, reports):
        # Compute the final test outcome
        outcomes = [report.outcome for report in reports]
        if "failed" in outcomes:
            outcome = "failed"
        elif "skipped" in outcomes:
            outcome = "skipped"
        else:
            outcome = "passed"

        # Errors
        error = ""
        errors = {}
        for report in reports:
            if report.outcome == "failed" and report.longrepr:
                if hasattr(report.longrepr, "toterminal"):
                    # Compute human repre
                    tw = TerminalWriter(stringio=True)
                    tw.hasmarkup = False
                    report.longrepr.toterminal(tw)
                    exc = tw.stringio.getvalue()
                else:
                    exc = str(report.longrepr)
                humanrepr = exc.strip()

                errors[report.when] = {"humanrepr": humanrepr}

                # Take the first error
                if not error:
                    error = humanrepr

        # Skipped
        skipped_messages = {}
        for report in reports:
            if report.outcome == "skipped" and report.longrepr:
                skipped_messages[report.when] = report.longrepr[2]

        # Durations
        total_duration = 0
        durations = {}
        for report in reports:
            durations[report.when] = report.duration
            total_duration += report.duration

        report = reports[-1]

        # Get logs reports
        logs = ""

        for secname, content in report.sections:
            if secname == "Captured log call":
                logs = content

        raw_json_report = {
            "_type": "test_result",
            "file": report.fspath,
            "line": report.location[1] + 1,  # Pytest lineno are 0-based
            "test_name": report.location[2],
            "duration": total_duration,
            "durations": durations,
            "outcome": outcome,
            "id": report.nodeid,
            "stdout": report.capstdout,
            "stderr": report.capstderr,
            "error": {
                "humanrepr": error
            },
            "logs": logs,
            "skipped_messages": skipped_messages,
        }
        output(raw_json_report)
Ejemplo n.º 22
0
 def toterminal(self, tw: TerminalWriter) -> None:
     tw.write("".join(self.lines))
Ejemplo n.º 23
0
 def toterminal(self, tw: TerminalWriter) -> None:
     for reprlocation, lines in self.reprlocation_lines:
         for line in lines:
             tw.line(line)
         reprlocation.toterminal(tw)
Ejemplo n.º 24
0
    def pytest_exception_interact(self, report, call):
        """
        Called when an exception was raised which can potentially be
        interactively handled.

        With the `--interactive` flag, outputs the full repr of the failed test
        and opens an interactive shell using `brownie._cli.console.Console`.

        Arguments
        ---------
        report : _pytest.reports.BaseReport
            Report object for the failed test.
        call : _pytest.runner.CallInfo
            Result/Exception info for the failed test.
        """
        if self.config.getoption("interactive") and report.failed:
            location = self._path(report.location[0])
            if location not in self.node_map:
                # if the exception happened prior to collection it is likely a
                # SyntaxError and we cannot open an interactive debugger
                return

            capman = self.config.pluginmanager.get_plugin("capturemanager")
            if capman:
                capman.suspend_global_capture(in_=True)

            tw = TerminalWriter()
            report.longrepr.toterminal(tw)

            # find last traceback frame within the active test
            excinfo = call.excinfo
            traceback = next(
                (i for i in excinfo.traceback[::-1]
                 if Path(i.path).as_posix().endswith(location)),
                excinfo.traceback[-1],
            )

            # get global namespace
            globals_dict = traceback.frame.f_globals

            # filter python internals and pytest internals
            globals_dict = {
                k: v
                for k, v in globals_dict.items() if not k.startswith("__")
            }
            globals_dict = {
                k: v
                for k, v in globals_dict.items() if not k.startswith("@")
            }

            # filter test functions and fixtures
            test_names = self.node_map[location]
            globals_dict = {
                k: v
                for k, v in globals_dict.items() if k not in test_names
            }
            globals_dict = {
                k: v
                for k, v in globals_dict.items()
                if not hasattr(v, "_pytestfixturefunction")
            }

            # get local namespace
            locals_dict = traceback.locals
            locals_dict = {
                k: v
                for k, v in locals_dict.items() if not k.startswith("@")
            }

            namespace = {"_callinfo": call, **globals_dict, **locals_dict}
            if "tx" not in namespace and brownie.history:
                # make it easier to look at the most recent transaction
                namespace["tx"] = brownie.history[-1]

            try:
                CONFIG.argv["cli"] = "console"
                shell = Console(self.project,
                                extra_locals=namespace,
                                exit_on_continue=True)
                banner = ("\nInteractive mode enabled. Type `continue` to"
                          " resume testing or `quit()` to halt execution.")
                shell.interact(banner=banner, exitmsg="")
            except SystemExit as exc:
                if exc.code != "continue":
                    pytest.exit("Test execution halted due to SystemExit")
            finally:
                CONFIG.argv["cli"] = "test"

            print("Continuing tests...")
            if capman:
                capman.resume_global_capture()
Ejemplo n.º 25
0
    def pytest_exception_interact(self, report, call):
        """
        Called when an exception was raised which can potentially be
        interactively handled.

        With the `--interactive` flag, outputs the full repr of the failed test
        and opens an interactive shell using `brownie._cli.console.Console`.

        Arguments
        ---------
        report : _pytest.reports.BaseReport
            Report object for the failed test.
        call : _pytest.runner.CallInfo
            Result/Exception info for the failed test.
        """
        if self.config.getoption("interactive") and report.failed:
            capman = self.config.pluginmanager.get_plugin("capturemanager")
            if capman:
                capman.suspend_global_capture(in_=True)

            tw = TerminalWriter()
            report.longrepr.toterminal(tw)
            location = self._path(report.location[0])

            # find last traceback frame within the active test
            traceback = next(
                (i for i in call.excinfo.traceback[::-1]
                 if i.path.strpath.endswith(location)),
                call.excinfo.traceback[-1],
            )

            # get global namespace
            globals_dict = traceback.frame.f_globals

            # filter python internals and pytest internals
            globals_dict = {
                k: v
                for k, v in globals_dict.items() if not k.startswith("__")
            }
            globals_dict = {
                k: v
                for k, v in globals_dict.items() if not k.startswith("@")
            }

            # filter test functions
            test_names = self.node_map[location]
            globals_dict = {
                k: v
                for k, v in globals_dict.items() if k not in test_names
            }

            # get local namespace
            locals_dict = traceback.locals
            locals_dict = {
                k: v
                for k, v in locals_dict.items() if not k.startswith("@")
            }

            namespace = {"_callinfo": call, **globals_dict, **locals_dict}

            try:
                CONFIG.argv["cli"] = "console"
                shell = Console(self.project, extra_locals=namespace)
                shell.interact(
                    banner=
                    f"\nInteractive mode enabled. Use quit() to continue running tests.",
                    exitmsg="",
                )
            except SystemExit:
                pass
            finally:
                CONFIG.argv["cli"] = "test"

            print("Continuing tests...")
            if capman:
                capman.resume_global_capture()
Ejemplo n.º 26
0
 def toterminal(self, out: TerminalWriter) -> None:
     out.line(self.longrepr, red=True)
Ejemplo n.º 27
0
 def toterminal(self, tw: TerminalWriter) -> None:
     for name, content, sep in self.sections:
         tw.sep(sep, name)
         tw.line(content)
Ejemplo n.º 28
0
 def toterminal(self, tw: TerminalWriter) -> None:
     for line in self.lines:
         tw.line(line)
Ejemplo n.º 29
0
def cacheshow(config: Config, session: Session) -> int:
    from pprint import pformat

    assert config.cache is not None

    tw = TerminalWriter()
    tw.line("cachedir: " + str(config.cache._cachedir))
    if not config.cache._cachedir.is_dir():
        tw.line("cache is empty")
        return 0

    glob = config.option.cacheshow[0]
    if glob is None:
        glob = "*"

    dummy = object()
    basedir = config.cache._cachedir
    vdir = basedir / Cache._CACHE_PREFIX_VALUES
    tw.sep("-", "cache values for %r" % glob)
    for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()):
        key = str(valpath.relative_to(vdir))
        val = config.cache.get(key, dummy)
        if val is dummy:
            tw.line("%s contains unreadable content, will be ignored" % key)
        else:
            tw.line("%s contains:" % key)
            for line in pformat(val).splitlines():
                tw.line("  " + line)

    ddir = basedir / Cache._CACHE_PREFIX_DIRS
    if ddir.is_dir():
        contents = sorted(ddir.rglob(glob))
        tw.sep("-", "cache directories for %r" % glob)
        for p in contents:
            # if p.check(dir=1):
            #    print("%s/" % p.relto(basedir))
            if p.is_file():
                key = str(p.relative_to(basedir))
                tw.line(f"{key} is a file of length {p.stat().st_size:d}")
    return 0
Ejemplo n.º 30
0
 def toterminal(self, tw: TerminalWriter) -> None:
     tw.line("я")