示例#1
0
def frame(source, line, **kwargs):
    """Matches DAP Frame objects.

    If source is py.path.local, it's automatically wrapped with some.dap.source().

    If line is unicode, it is treated as a line marker, and translated to a line
    number via get_marked_line_numbers(source["path"]) if possible.
    """

    if isinstance(source, py.path.local):
        source = some.dap.source(source)

    if isinstance(line, unicode):
        if isinstance(source, dict):
            path = source["path"]
        elif isinstance(source, _impl.DictContaining):
            path = source.items["path"]
        else:
            path = None
        assert isinstance(
            path, _impl.Path
        ), "source must be some.dap.source() to use line markers in some.dap.frame()"
        line = code.get_marked_line_numbers(path.path)[line]

    d = {"id": some.dap.id, "source": source, "line": line, "column": 1}
    d.update(kwargs)
    return some.dict.containing(d)
示例#2
0
    def set_breakpoints(self, path, lines):
        """Sets breakpoints in the specified file, and returns the list of all the
        corresponding DAP Breakpoint objects in the same order.

        If lines are specified, it should be an iterable in which every element is
        either a line number or a string. If it is a string, then it is translated
        to the corresponding line number via get_marked_line_numbers(path).

        If lines=all, breakpoints will be set on all the marked lines in the file.
        """

        # Don't fetch line markers unless needed - in some cases, the breakpoints
        # might be set in a file that does not exist on disk (e.g. remote attach).
        get_marked_line_numbers = lambda: code.get_marked_line_numbers(path)

        if lines is all:
            lines = get_marked_line_numbers().keys()

        def make_breakpoint(line):
            if isinstance(line, int):
                descr = str(line)
            else:
                marker = line
                line = get_marked_line_numbers()[marker]
                descr = fmt("{0} (@{1})", line, marker)
            bp_log.append((line, descr))
            return {"line": line}

        bp_log = []
        breakpoints = self.request(
            "setBreakpoints",
            {
                "source": {
                    "path": path
                },
                "breakpoints": [make_breakpoint(line) for line in lines],
            },
        )("breakpoints", json.array())

        bp_log = sorted(bp_log, key=lambda pair: pair[0])
        bp_log = ", ".join((descr for _, descr in bp_log))
        log.info("Breakpoints set in {0}: {1}", path, bp_log)

        return breakpoints
示例#3
0
    def factory(source):
        assert isinstance(source, types.FunctionType)
        name = source.__name__
        source, _ = inspect.getsourcelines(source)

        # First, find the "def" line.
        def_lineno = 0
        for line in source:
            line = line.strip()
            if line.startswith("def") and line.endswith(":"):
                break
            def_lineno += 1
        else:
            raise ValueError("Failed to locate function header.")

        # Remove everything up to and including "def".
        source = source[def_lineno + 1:]
        assert source

        # Now we need to adjust indentation. Compute how much the first line of
        # the body is indented by, then dedent all lines by that amount. Blank
        # lines don't matter indentation-wise, and might not be indented to begin
        # with, so just replace them with a simple newline.
        line = source[0]
        indent = len(line) - len(line.lstrip())
        source = [l[indent:] if l.strip() else "\n" for l in source]
        source = "".join(source)

        # Write it to file.
        tmpfile = long_tmpdir / (name + ".py")
        tmpfile.strpath = compat.filename(tmpfile.strpath)
        assert not tmpfile.check()
        tmpfile.write(source)

        tmpfile.lines = code.get_marked_line_numbers(tmpfile)
        return tmpfile
示例#4
0
class lines:
    app_py = code.get_marked_line_numbers(paths.app_py)
示例#5
0
def test_exceptions_and_partial_exclude_rules(pyfile, target, run, scenario):
    @pyfile
    def code_to_debug():
        from debug_me import backchannel
        import sys

        call_me_back_dir = backchannel.receive()
        sys.path.insert(0, call_me_back_dir)

        import call_me_back

        def call_func():
            raise RuntimeError("unhandled error")  # @raise

        call_me_back.call_me_back(call_func)  # @call_me_back
        print("done")

    call_me_back_dir = test_data / "call_me_back"
    call_me_back_py = call_me_back_dir / "call_me_back.py"
    call_me_back_py.lines = code.get_marked_line_numbers(call_me_back_py)

    if scenario == "exclude_code_to_debug":
        rules = [{"path": "**/" + code_to_debug.basename, "include": False}]
    elif scenario == "exclude_callback_dir":
        rules = [{"path": call_me_back_dir, "include": False}]
    else:
        pytest.fail(scenario)
    log.info("Rules: {0!j}", rules)

    with debug.Session() as session:
        session.expected_exit_code = some.int
        session.config["rules"] = rules

        backchannel = session.open_backchannel()
        with run(session, target(code_to_debug)):
            session.request(
                "setExceptionBreakpoints", {"filters": ["raised", "uncaught"]}
            )

        backchannel.send(call_me_back_dir)

        if scenario == "exclude_code_to_debug":
            # Stop at handled exception, with code_to_debug.py excluded.
            #
            # Since the module raising the exception is excluded, it must not stop at
            # @raise, but rather at @callback (i.e. the closest non-excluded frame).

            stop = session.wait_for_stop(
                "exception",
                expected_frames=[
                    some.dap.frame(
                        some.dap.source(call_me_back_py),
                        line=call_me_back_py.lines["callback"],
                    )
                ],
            )
            assert stop.frames != some.list.containing(
                [some.dap.frame(some.dap.source(code_to_debug), line=some.int)]
            )

            # As exception unwinds the stack, we shouldn't stop at @call_me_back,
            # since that line is in the excluded file. Furthermore, although the
            # exception is unhandled, we shouldn't get a stop for that, either,
            # because the exception is last seen in an excluded file.
            session.request_continue()

        elif scenario == "exclude_callback_dir":
            # Stop at handled exception, with call_me_back.py excluded.
            #
            # Since the module raising the exception is not excluded, it must stop at
            # @raise.

            stop = session.wait_for_stop(
                "exception",
                expected_frames=[
                    some.dap.frame(
                        some.dap.source(code_to_debug),
                        name="call_func",
                        line=code_to_debug.lines["raise"],
                    ),
                    some.dap.frame(
                        some.dap.source(code_to_debug),
                        name="<module>",
                        line=code_to_debug.lines["call_me_back"],
                    ),
                ],
            )
            assert stop.frames != some.list.containing(
                [some.dap.frame(some.dap.source(call_me_back_py), line=some.int)]
            )

            session.request_continue()

            # As exception unwinds the stack, it must not stop at @callback, since that
            # line is in the excluded file. However, it must stop at @call_me_back.
            stop = session.wait_for_stop(
                "exception",
                expected_frames=[
                    some.dap.frame(
                        some.dap.source(code_to_debug),
                        name="<module>",
                        line=code_to_debug.lines["call_me_back"],
                    )
                ],
            )
            assert stop.frames != some.list.containing(
                [some.dap.frame(some.dap.source(call_me_back_py), line=some.int)]
            )

            session.request_continue()

            # Now the exception is unhandled, and should be reported as such.
            stop = session.wait_for_stop(
                "exception",
                expected_frames=[
                    some.dap.frame(
                        some.dap.source(code_to_debug),
                        name="call_func",
                        line=code_to_debug.lines["raise"],
                    ),
                    some.dap.frame(
                        some.dap.source(code_to_debug),
                        name="<module>",
                        line=code_to_debug.lines["call_me_back"],
                    ),
                ],
            )
            assert stop.frames != some.list.containing(
                [some.dap.frame(some.dap.source(call_me_back_py), line=some.int)]
            )

            # Let the process crash due to unhandled exception.
            session.request_continue()

        else:
            pytest.fail(scenario)