Exemple #1
0
 def wrappedfn(*a, **kw):
     try:
         return fn(*a, **kw)
     except AssertionError as e:
         # TODO: check that this isn't failing at an early line in a different
         # file?
         _, lineno = frame_summary_for_fn(
             fn, traceback.extract_tb(e.__traceback__))
         if lineno >= first_body_line:
             raise
Exemple #2
0
def attempt_call(conditions: Conditions, fn: Callable,
                 short_circuit: ShortCircuitingContext,
                 enforced_conditions: EnforcedConditions) -> CallAnalysis:
    space = context_statespace()
    bound_args = gen_args(conditions.sig)

    msg_gen = MessageGenerator(fn)
    with space.framework():
        # TODO: looks wrong(-ish) to guard this with space.framework().
        # Copy on custom objects may require patched builtins. (datetime.timedelta is one such case)
        original_args = copy.deepcopy(bound_args)
    space.checkpoint()

    lcls: Mapping[str, object] = bound_args.arguments
    # In preconditions, __old__ exists but is just bound to the same args.
    # This lets people write class invariants using `__old__` to, for example,
    # demonstrate immutability.
    lcls = {'__old__': AttributeHolder(lcls), **lcls}
    expected_exceptions = conditions.raises
    for precondition in conditions.pre:
        with ExceptionFilter(expected_exceptions) as efilter:
            with enforced_conditions.enabled_enforcement(), short_circuit:
                precondition_ok = precondition.evaluate(lcls)
            if not precondition_ok:
                debug('Failed to meet precondition', precondition.expr_source)
                return CallAnalysis(failing_precondition=precondition)
        if efilter.ignore:
            debug('Ignored exception in precondition.', efilter.analysis)
            return efilter.analysis
        elif efilter.user_exc is not None:
            (user_exc, tb) = efilter.user_exc
            debug('Exception attempting to meet precondition',
                  precondition.expr_source, ':', user_exc, tb.format())
            return CallAnalysis(
                failing_precondition=precondition,
                failing_precondition_reason=
                f'it raised "{repr(user_exc)} at {tb.format()[-1]}"')

    with ExceptionFilter(expected_exceptions) as efilter:
        with enforced_conditions.enabled_enforcement(), short_circuit:
            assert not space.running_framework_code
            __return__ = fn(*bound_args.args, **bound_args.kwargs)
        lcls = {
            **bound_args.arguments, '__return__': __return__,
            '_': __return__,
            '__old__': AttributeHolder(original_args.arguments),
            fn.__name__: fn
        }

    if efilter.ignore:
        debug('Ignored exception in function.', efilter.analysis)
        return efilter.analysis
    elif efilter.user_exc is not None:
        space.check_deferred_assumptions()
        (e, tb) = efilter.user_exc
        detail = name_of_type(type(e)) + ': ' + str(e)
        frame_filename, frame_lineno = frame_summary_for_fn(tb, fn)
        debug('exception while evaluating function body:', detail,
              frame_filename, 'line', frame_lineno)
        detail += ' ' + get_input_description(fn.__name__, original_args,
                                              _MISSING)
        return CallAnalysis(VerificationStatus.REFUTED, [
            msg_gen.make(MessageType.EXEC_ERR, detail, frame_filename,
                         frame_lineno, ''.join(tb.format()))
        ])

    for argname, argval in bound_args.arguments.items():
        if (conditions.mutable_args is not None
                and argname not in conditions.mutable_args):
            old_val, new_val = original_args.arguments[argname], argval
            if not deep_eq(old_val, new_val, set()):
                space.check_deferred_assumptions()
                detail = 'Argument "{}" is not marked as mutable, but changed from {} to {}'.format(
                    argname, old_val, new_val)
                debug('Mutablity problem:', detail)
                return CallAnalysis(
                    VerificationStatus.REFUTED,
                    [msg_gen.make(MessageType.POST_ERR, detail, None, 0, '')])

    (post_condition, ) = conditions.post
    with ExceptionFilter(expected_exceptions) as efilter:
        # TODO: re-enable post-condition short circuiting. This will require refactoring how
        # enforced conditions and short curcuiting interact, so that post-conditions are
        # selectively run when, and only when, performing a short circuit.
        #with enforced_conditions.enabled_enforcement(), short_circuit:
        isok = bool(post_condition.evaluate(lcls))
    if efilter.ignore:
        debug('Ignored exception in postcondition.', efilter.analysis)
        return efilter.analysis
    elif efilter.user_exc is not None:
        space.check_deferred_assumptions()
        (e, tb) = efilter.user_exc
        detail = repr(e) + ' ' + get_input_description(
            fn.__name__, original_args, __return__,
            post_condition.addl_context)
        debug('exception while calling postcondition:', detail)
        failures = [
            msg_gen.make(MessageType.POST_ERR, detail, post_condition.filename,
                         post_condition.line, ''.join(tb.format()))
        ]
        return CallAnalysis(VerificationStatus.REFUTED, failures)
    if isok:
        debug('Postcondition confirmed.')
        return CallAnalysis(VerificationStatus.CONFIRMED)
    else:
        space.check_deferred_assumptions()
        detail = 'false ' + \
                 get_input_description(
                     fn.__name__, original_args, __return__, post_condition.addl_context)
        debug(detail)
        failures = [
            msg_gen.make(MessageType.POST_FAIL, detail,
                         post_condition.filename, post_condition.line, '')
        ]
        return CallAnalysis(VerificationStatus.REFUTED, failures)