Exemplo n.º 1
0
def check_post_err(
    fn: Callable, optionset: AnalysisOptionSet = AnalysisOptionSet()
) -> ComparableLists:
    local_opts = AnalysisOptionSet(max_iterations=20)
    options = local_opts.overlay(optionset)
    states = [m.state for m in run_checkables(analyze_function(fn, options))]
    return (states, [MessageType.POST_ERR])
Exemplo n.º 2
0
def collect_options(thing: Any) -> AnalysisOptionSet:
    parent_opts = AnalysisOptionSet()
    is_package = thing.__name__ == getattr(thing, "__package__", None)
    if getattr(thing, "__module__", None):
        parent_opts = collect_options(sys.modules[thing.__module__])
    elif getattr(thing, "__package__", None):
        if is_package:
            parent_pkg, _, _ = thing.__package__.rpartition(".")
        else:
            parent_pkg = thing.__package__
        if parent_pkg:
            parent_opts = collect_options(sys.modules[parent_pkg])

    lines: Iterable[str]
    if is_package:
        try:
            lines = importlib.resources.read_text(thing,
                                                  "__init__.py").splitlines()
        except FileNotFoundError:
            lines = []
    else:
        _file, _start, lines = sourcelines(thing)
    directives = get_directives(lines)
    if inspect.ismodule(thing):
        # Only look at toplevel comments in modules
        # (we don't want to catch directives for functions inside it)
        # TODO: detect directives at other levels like classes etc and warn that they
        # will be ignored.
        directives = [(l, c, t) for (l, c, t) in directives if c == 0]
    my_opts = parse_directives(directives)
    return parent_opts.overlay(my_opts)
Exemplo n.º 3
0
def check_fail(
    fn: Callable, optionset: AnalysisOptionSet = AnalysisOptionSet()
) -> ComparableLists:
    local_opts = AnalysisOptionSet(max_iterations=40, per_condition_timeout=5)
    options = local_opts.overlay(optionset)
    states = [m.state for m in run_checkables(analyze_function(fn, options))]
    return (states, [MessageType.POST_FAIL])
Exemplo n.º 4
0
def parse_directives(
        directive_lines: Iterable[Tuple[int, int, str]]) -> AnalysisOptionSet:
    """
    Parse options from directives in comments.

    >>> parse_directives([(1, 0, "off")]).enabled
    False
    """
    result = AnalysisOptionSet()
    for lineno, _colno, directive in directive_lines:
        for part in directive.split():
            if part == "on":
                part = "enabled=1"
            if part == "off":
                part = "enabled="
            pair = part.split("=", 2)
            if len(pair) != 2:
                raise InvalidDirective(f'Malformed option: "{part}"', lineno)
            key, strvalue = pair
            if key not in AnalysisOptionSet.directive_fields:
                raise InvalidDirective(f'Unknown option: "{key}"', lineno)
            value = AnalysisOptionSet.parse_field(key, strvalue)
            if value is None:
                raise InvalidDirective(
                    f'"{strvalue}" is not a valid "{key}" value', lineno)
            if getattr(result, key) is not None:
                raise InvalidDirective(
                    f'Option "{key}" is set multiple times at the same scope',
                    lineno)
            result = result.overlay(AnalysisOptionSet(**{key: value}))
    return result
Exemplo n.º 5
0
def check_unknown(
    fn: Callable, optionset: AnalysisOptionSet = AnalysisOptionSet()
) -> ComparableLists:
    local_opts = AnalysisOptionSet(max_iterations=40, per_condition_timeout=3)
    options = local_opts.overlay(optionset)
    messages = [(m.state, m.message, m.traceback)
                for m in run_checkables(analyze_function(fn, options))]
    return (messages, [(MessageType.CANNOT_CONFIRM, "Not confirmed.", "")])
Exemplo n.º 6
0
def check_ok(
    fn: Callable, optionset: AnalysisOptionSet = AnalysisOptionSet()
) -> ComparableLists:
    local_opts = AnalysisOptionSet(per_condition_timeout=5)
    options = local_opts.overlay(optionset)
    messages = [
        message for message in run_checkables(analyze_function(fn, options))
        if message.state != MessageType.CONFIRMED
    ]
    return (messages, [])
Exemplo n.º 7
0
def check_exec_err(
    fn: Callable, message_prefix="", optionset: AnalysisOptionSet = AnalysisOptionSet()
) -> ComparableLists:
    local_opts = AnalysisOptionSet(max_iterations=20, per_condition_timeout=5)
    options = local_opts.overlay(optionset)
    messages = run_checkables(analyze_function(fn, options))
    if all(m.message.startswith(message_prefix) for m in messages):
        return ([m.state for m in messages], [MessageType.EXEC_ERR])
    else:
        return (
            [(m.state, m.message) for m in messages],
            [(MessageType.EXEC_ERR, message_prefix)],
        )
Exemplo n.º 8
0
    def run_iteration(
        self,
        max_condition_timeout=0.5
    ) -> Iterator[Tuple[Counter[str], List[AnalysisMessage]]]:
        debug(f"starting pass "
              f"with a condition timeout of {max_condition_timeout}")
        debug("Files:", self._modtimes.keys())
        pool = self._pool
        for filename in self._modtimes.keys():
            worker_timeout = max(10.0, max_condition_timeout * 20.0)
            iter_options = AnalysisOptionSet(
                per_condition_timeout=max_condition_timeout,
                per_path_timeout=max_condition_timeout / 4,
            )
            options = self._options.overlay(iter_options)
            pool.submit((filename, options, time.time() + worker_timeout))

        pool.garden_workers()
        while pool.is_working():
            result = pool.get_result(timeout=1.0)
            if result is not None:
                (_, counters, messages) = result
                yield (counters, messages)
                if pool.has_result():
                    continue
            change_detected = self.check_changed()
            if change_detected:
                self._change_flag = True
                debug("Aborting iteration on change detection")
                pool.terminate()
                self._pool = self.startpool()
                return
            pool.garden_workers()
        debug("Worker pool tasks complete")
        yield (Counter(), [])
Exemplo n.º 9
0
def unwalled_main(cmd_args: Union[List[str], argparse.Namespace]) -> None:
    if isinstance(cmd_args, argparse.Namespace):
        args = cmd_args
    else:
        args = command_line_parser().parse_args(cmd_args)
    set_debug(args.verbose)
    options = option_set_from_dict(args.__dict__)
    if sys.path and sys.path[0] != "":
        # fall back to current directory to look up modules
        sys.path.append("")
    if args.action == "check":
        exitcode = check(args, options, sys.stdout, sys.stderr)
    elif args.action == "diffbehavior":
        defaults = DEFAULT_OPTIONS.overlay(
            AnalysisOptionSet(
                per_condition_timeout=2.5,
                per_path_timeout=30.0,  # mostly, we don't want to time out paths
            ))
        exitcode = diffbehavior(args, defaults.overlay(options), sys.stdout,
                                sys.stderr)
    elif args.action == "watch":
        exitcode = watch(args, options)
    else:
        print(f'Unknown action: "{args.action}"', file=sys.stderr)
        exitcode = 2
    sys.exit(exitcode)
Exemplo n.º 10
0
 def test_static_method(self) -> None:
     messages = analyze_any(
         walk_qualname(Person, "a_static_method"),
         AnalysisOptionSet(per_condition_timeout=5),
     )
     self.assertEqual(
         *check_messages(messages, state=MessageType.CONFIRMED))
Exemplo n.º 11
0
    def test_symbolic_types_without_literal_types(self) -> None:
        def f(typ1: Type, typ2: Type, typ3: Type):
            """ post: implies(_, issubclass(typ1, typ3)) """
            return issubclass(typ2, typ3) and typ2 != typ3

        self.assertEqual(*check_fail(
            f, AnalysisOptionSet(max_iterations=60, per_condition_timeout=10)))
Exemplo n.º 12
0
    def TODO_test_subtype_union(self) -> None:
        def f(s: Set[Union[int, str]]) -> Set[Union[int, str]]:
            """ post: not ((42 in s) and ('42' in s)) """
            return s

        self.assertEqual(
            *check_fail(f, AnalysisOptionSet(per_condition_timeout=7.0)))
Exemplo n.º 13
0
        def test_icontract_basic(self):
            @icontract.ensure(lambda result, x: result > x)
            def some_func(x: int, y: int = 5) -> int:
                return x - y

            self.assertEqual(*check_fail(
                some_func,
                AnalysisOptionSet(analysis_kind=[AnalysisKind.icontract])))
    def test_datetime_fail(self) -> None:
        def f(dtime: datetime.datetime) -> int:
            """
            post: _ != 22
            """
            return dtime.second

        self.assertEqual(*check_fail(f, AnalysisOptionSet(max_iterations=60)))
Exemplo n.º 15
0
        def test_icontract_weaken(self):
            @icontract.require(lambda x: x in (2, 3))
            @icontract.ensure(lambda: True)
            def trynum(x: int):
                IcontractB().weakenedfunc(x)

            self.assertEqual(*check_ok(
                trynum,
                AnalysisOptionSet(analysis_kind=[AnalysisKind.icontract])))
Exemplo n.º 16
0
        def test_nonuniform_list_types_2(self) -> None:
            def f(a: Set[FrozenSet[int]]) -> object:
                """
                pre: a == {frozenset({7}), frozenset({42})}
                post: _ in ('{frozenset({7}), frozenset({42})}', '{frozenset({42}), frozenset({7})}')
                """
                return repr(a)

            check_ok(f, AnalysisOptionSet(per_path_timeout=5, per_condition_timeout=5))
Exemplo n.º 17
0
 def test_methods_directly(self) -> None:
     # Running analysis on individual methods directly works a little
     # differently, especially for staticmethod/classmethod. Confirm these
     # don't explode:
     messages = analyze_any(
         walk_qualname(Person, "a_regular_method"),
         AnalysisOptionSet(per_condition_timeout=5),
     )
     self.assertEqual(*check_messages(messages, state=MessageType.CONFIRMED))
Exemplo n.º 18
0
    def test_compare_ok(self) -> None:
        def f(a: str, b: str) -> bool:
            """
            pre: a and b
            post: implies(__return__, a[0] <= b[0])
            """
            return a < b

        self.assertEqual(*check_ok(f, AnalysisOptionSet(per_path_timeout=5)))
Exemplo n.º 19
0
def collect_options(thing: Any) -> AnalysisOptionSet:
    parent_opts = AnalysisOptionSet()
    if getattr(thing, "__module__", None):
        parent_opts = collect_options(sys.modules[thing.__module__])
    elif hasattr(thing, "__package__"):
        if thing.__package__ and thing.__package__ != thing.__name__:
            parent_opts = collect_options(sys.modules[thing.__package__])

    _file, start, lines = sourcelines(thing)
    directives = get_directives(lines)
    if inspect.ismodule(thing):
        # Only look at toplevel comments in modules
        # (we don't want to catch directives for functions inside it)
        # TODO: detect directives at other levels like classes etc and warn that they
        # will be ignored.
        directives = [(l, c, t) for (l, c, t) in directives if c == 0]
    my_opts = parse_directives(directives)
    return parent_opts.overlay(my_opts)
Exemplo n.º 20
0
def test_builtin(fn_name: str) -> None:
    opts = AnalysisOptionSet(
        max_iterations=20, per_condition_timeout=20, per_path_timeout=5
    )
    this_module = sys.modules[__name__]
    fn = getattr(this_module, fn_name)
    messages = run_checkables(analyze_function(fn, opts))
    errors = [m for m in messages if m.state > MessageType.PRE_UNSAT]
    assert errors == []
    def test_symbolic_months_fail(self) -> None:
        def f(num_months: int) -> datetime.date:
            """
            pre: 0 <= num_months <= 100
            post: _.year != 2003
            """
            dt = datetime.date(2000, 1, 1)
            return dt + datetime.timedelta(days=30 * num_months)

        self.assertEqual(*check_fail(f, AnalysisOptionSet(
            per_path_timeout=10)))
Exemplo n.º 22
0
    def test_sets_eq(self) -> None:
        def f(a: Set[FrozenSet[int]]) -> object:
            """
            pre: a == {frozenset({7}), frozenset({42})}
            post: _ in ('{frozenset({7}), frozenset({42})}', '{frozenset({42}), frozenset({7})}')
            """
            return repr(a)

        self.assertEqual(*check_ok(
            f, AnalysisOptionSet(per_path_timeout=10,
                                 per_condition_timeout=10)))
Exemplo n.º 23
0
    def test_number_parse(self) -> None:
        number_re = re.compile(r"(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?")

        def f(s: str):
            """
            pre: len(s) == 4
            post: not _
            """
            return bool(number_re.fullmatch(s))

        self.assertEqual(*check_fail(
            f, AnalysisOptionSet(max_iterations=20, per_condition_timeout=10)))
Exemplo n.º 24
0
    def test_csv_example(self) -> None:
        def f(lines: List[str]) -> List[str]:
            """
            pre: all(',' in line for line in lines)
            post: __return__ == [line.split(',')[0] for line in lines]
            """
            return [line[:line.index(",")] for line in lines]

        # TODO: the model generation doesn't work right here (getting a lot of empty strings):
        options = AnalysisOptionSet(per_path_timeout=0.5,
                                    per_condition_timeout=5)
        self.assertEqual(*check_unknown(f, options))
Exemplo n.º 25
0
def unwalled_main(cmd_args: Union[List[str], argparse.Namespace]) -> int:
    parser = command_line_parser()
    if isinstance(cmd_args, argparse.Namespace):
        args = cmd_args
    else:
        args = parser.parse_args(cmd_args)
    if not args.action:
        parser.print_help(sys.stderr)
        return 2
    set_debug(args.verbose)
    debug("Installed plugins:", installed_plugins)
    options = option_set_from_dict(args.__dict__)
    # fall back to current directory to look up modules
    with add_to_pypath(*([""] if sys.path and sys.path[0] != "" else [])):
        if args.action == "check":
            return check(args, options, sys.stdout, sys.stderr)
        elif args.action == "diffbehavior":
            defaults = DEFAULT_OPTIONS.overlay(
                AnalysisOptionSet(
                    per_condition_timeout=2.5,
                    per_path_timeout=
                    30.0,  # mostly, we don't want to time out paths
                ))
            return diffbehavior(args, defaults.overlay(options), sys.stdout,
                                sys.stderr)
        elif args.action == "cover":
            defaults = DEFAULT_OPTIONS.overlay(
                AnalysisOptionSet(
                    per_condition_timeout=2.5,
                    per_path_timeout=
                    30.0,  # mostly, we don't want to time out paths
                ))
            return cover(args, defaults.overlay(options), sys.stdout,
                         sys.stderr)
        elif args.action == "watch":
            return watch(args, options)
        else:
            print(f'Unknown action: "{args.action}"', file=sys.stderr)
            return 2
Exemplo n.º 26
0
        def test_icontract_nesting(self):
            @icontract.require(lambda name: name.startswith("a"))
            def innerfn(name: str):
                pass

            @icontract.ensure(lambda: True)
            @icontract.require(lambda name: len(name) > 0)
            def outerfn(name: str):
                innerfn("00" + name)

            self.assertEqual(*check_exec_err(
                outerfn,
                message_prefix="PreconditionFailed",
                optionset=AnalysisOptionSet(
                    analysis_kind=[AnalysisKind.icontract]),
            ))
Exemplo n.º 27
0
    def test_getattr(self) -> None:
        class Otter:
            def do_cute_human_things_with_hands(self) -> str:
                return "cuteness"

        def f(s: str) -> str:
            """ post: _ != "cuteness" """
            try:
                return getattr(Otter(), s)()
            except:
                return ""

        messages = run_checkables(
            analyze_function(
                f, AnalysisOptionSet(max_iterations=20,
                                     per_condition_timeout=5)))
        self.assertEqual(len(messages), 1)
        self.assertEqual(
            messages[0].message,
            "false when calling f(s = 'do_cute_human_things_with_hands') (which returns 'cuteness')",
        )
Exemplo n.º 28
0
def test_builtins() -> None:
    opts = AnalysisOptionSet(max_iterations=5, per_condition_timeout=10)
    messages = run_checkables(analyze_module(sys.modules[__name__], opts))
    errors = [m for m in messages if m.state > MessageType.PRE_UNSAT]
    assert errors == []
Exemplo n.º 29
0
def check_states(
    fn: Callable, optionset: AnalysisOptionSet = AnalysisOptionSet()
) -> Set[MessageType]:
    local_opts = AnalysisOptionSet(max_iterations=40, per_condition_timeout=5)
    options = local_opts.overlay(optionset)
    return set([m.state for m in run_checkables(analyze_function(fn, options))])