def prehook(self, boundargs, ctx: WrappedFunctionContextProvider): for p in self.precondition: bindings = {} for name in inspect.signature(p).parameters: if name in boundargs.arguments: bindings[name] = boundargs.arguments[name] else: raise UntypyAttributeError( f"Did not find argument {name} of precondition in function.", locations=[ WrappedFunction.find_location(p), WrappedFunction.find_location(self.func), ] ) if not p(**bindings): lsource = find_lambdasource(p) if lsource is not None: expected = f"passing: {lsource}" else: expected = "passing precondition" err = UntypyTypeError( bindings, expected ) err = err.with_note("Failed precondition.") err = err.with_frame(Frame( expected, "", declared=WrappedFunction.find_location(p), responsable=None, )) raise ctx(0).wrap(err)
def posthook(self, ret, boundargs, ctx: ExecutionContext): for p in self.postcondition: bindings = {} for name in inspect.signature(p).parameters: if name == "ret": bindings["ret"] = ret elif name in boundargs.arguments: bindings[name] = boundargs.arguments[name] else: raise UntypyAttributeError( f"Did not find argument {name} of postcondition in function.", locations=[ WrappedFunction.find_location(p), WrappedFunction.find_location(self.func), ] ) if not p(**bindings): lsource = find_lambdasource(p) if lsource is not None: expected = f"passing: {lsource}" else: expected = "passing postcondition" given = ret.__repr__() err = UntypyTypeError( given, expected, ).with_note("Failed postcondition").with_frame(Frame( expected, "", declared=WrappedFunction.find_location(p), responsable=None, )) raise ctx.wrap(err)
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: (next_ty, indicator) = err.next_type_and_indicator() return_id = IndicatorStr(next_ty, indicator) original = WrappedFunction.find_original(self.fn) try: signature = inspect.signature(original) # TODO:!!! FIX BUILTINS front_sig = [] for name in signature.parameters: front_sig.append( f"{name}: {self.fn.checker_for(name).describe()}") front_sig = f"{original.__name__}(" + ( ", ".join(front_sig)) + ") -> " return_id = IndicatorStr(front_sig) + return_id except: return_id = IndicatorStr("???") declared = WrappedFunction.find_location(self.fn) return err.with_frame( Frame( return_id.ty, return_id.indicator, declared=declared, responsable=declared, ))
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: (original_expected, _ind) = err.next_type_and_indicator() err = ArgumentExecutionContext(self.tc.inner, None, self.arg_name).wrap(err) func_decl = WrappedFunction.find_location(self.tc.inner) name = WrappedFunction.find_original(self.tc.inner).__name__ (decl, ind) = err.next_type_and_indicator() err = err.with_frame( Frame(decl, ind, declared=None, responsable=func_decl)) previous_chain = UntypyTypeError( f"def {name}{self.tc.inner.describe()}", f"{self.tc.describe()}") (decl, ind) = previous_chain.next_type_and_indicator() previous_chain = previous_chain.with_frame( Frame(decl, ind, declared=func_decl, responsable=None)) err = err.with_note( f"The argument '{self.arg_name}' of method '{name}' violates the signature of {self.tc.describe()}." ) previous_chain = self.upper.wrap(previous_chain) return err.with_previous_chain(previous_chain)
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: (next_ty, indicator) = err.next_type_and_indicator() error_id = IndicatorStr(next_ty, indicator) original = WrappedFunction.find_original(self.fn) try: signature = inspect.signature(original) except ValueError: # fails on some built-ins signature = inspect.signature(self.fn) wf = None if (hasattr(self.fn, '__wf')): wf = getattr(self.fn, '__wf') elif isinstance(self.fn, WrappedFunction): wf = self.fn arglist = [] for name in signature.parameters: if name is self.argument_name: arglist.append(IndicatorStr(f"{name}: ") + error_id) else: if wf is not None: arglist.append( IndicatorStr( f"{name}: {wf.checker_for(name).describe()}")) else: arglist.append(IndicatorStr(f"{name}")) id = IndicatorStr(f"{format_name(original)}(") + IndicatorStr( ", ").join(arglist) if wf is not None: id += IndicatorStr(f") -> {wf.checker_for('return').describe()}") else: id += IndicatorStr(f")") if self.declared is None: declared = WrappedFunction.find_location(self.fn) else: declared = self.declared if self.stack is not None: responsable = Location.from_stack(self.stack) else: responsable = None frame = Frame(id.ty, id.indicator, declared=declared, responsable=responsable) return err.with_frame(frame)
def analyse(item, ctx: CreationContext, e) -> UntypyAttributeError: org = WrappedFunction.find_original(item) source = inspect.getsource(item) fn_ast = ast.parse(source) for node in map_str_to_ast(fn_ast.body[0].args, fn_ast.body[0].returns): for rule in RULES: rule_result = rule(node) if rule_result: # Got a Match (n, message) = rule_result display = DisplayMatrix(source) display.write((n.col_offset - 1, n.lineno), " " + "^" * (n.end_col_offset - n.col_offset) + " - " + message) return ctx.wrap( UntypyAttributeError( f"Type annotation of function '{qualname(org)}' could not be resolved:\n" f"{e}\n" f"\n{display}")) return ctx.wrap( UntypyAttributeError( f"Type annotation of function '{qualname(org)}' could not be resolved:\n" f"{e}\n"))
def find_lambdasource(fn) -> Optional[str]: """ tries to retuns body of precondition or postcondition annotation """ try: fn = WrappedFunction.find_original(fn) source = inspect.getsource(fn).split('\n') m = re.match('@[a-zA-Z_\.]+\((.*)\)', source[0]) if m is not None and len(m.groups()) == 1: return m.group(1) except: return None
def __init__(self, inner: Callable, return_checker: TypeChecker, argument_checker: list[TypeChecker], ctx: ExecutionContext): # unwrap if wrapped function self.ResponsibilityType = None if hasattr(inner, '__wf'): inner = getattr(inner, '__wf') self.inner = inner self.return_checker = return_checker self.argument_checker = argument_checker self.ctx = ctx self.fn = WrappedFunction.find_original(self.inner) setattr(self, '__wf', self)
def wrap_function(fn: FunctionType, cfg: Config) -> Callable: if len(inspect.getfullargspec(fn).annotations) > 0: if cfg.verbose: print(f"Patching Function: {fn.__name__}") return TypedFunctionBuilder( fn, DefaultCreationContext( typevars=dict(), declared_location=WrappedFunction.find_location(fn), checkedpkgprefixes=cfg.checkedprefixes, eval_context=fn.__globals__)).build() else: return fn
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: (next_ty, indicator) = err.next_type_and_indicator() return_id = IndicatorStr(next_ty, indicator) original = WrappedFunction.find_original(self.fn) try: signature = inspect.signature(original) # TODO:!!! FIX BUILTINS front_sig = [] for name in signature.parameters: front_sig.append( f"{name}: {self.fn.checker_for(name).describe()}") front_sig = f"{format_name(original)}(" + ( ", ".join(front_sig)) + ") -> " return_id = IndicatorStr(front_sig) + return_id except: return_id = IndicatorStr("???") declared = WrappedFunction.find_location(self.fn) responsable = declared if responsable is not None: if err.expected is not None and err.given is None: # Missing Return-Value? err = err.with_note("Did you miss a return statement?") last_line = responsable.line_no + responsable.line_span - 1 responsable = responsable.narrow_in_span( (responsable.file, last_line)) else: responsable = responsable.narrow_in_span(self.reti_loc) return err.with_frame( Frame( return_id.ty, return_id.indicator, declared=declared, responsable=responsable, ))
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: (type_declared, indicator_line) = self.declared_and_indicator(err) declared = WrappedFunction.find_location(self.fn.inner) responsable = Location.from_stack(self.stack) frame = Frame( type_declared, indicator_line, declared=declared, responsable=responsable, ) err = err.with_frame(frame) err = err.with_inverted_responsibility_type() return self.upper.wrap(err)
def check(self, arg: Any, ctx: ExecutionContext) -> None: res = self.callable(arg) if not res: # raise error on falsy value err = UntypyTypeError(given=arg, expected=self.annotated.describe()) err = err.with_note( f"\n\nNote: Assertion in Callable failed with {repr(res)}.") (t, i) = err.next_type_and_indicator() err = err.with_frame( Frame(t, i, WrappedFunction.find_location(self.callable), None)) for info in self.annotated.info: err = err.with_note(" - " + info) raise ctx.wrap(err)
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: if self.invert: err = ReturnExecutionContext(self.fn.inner).wrap(err) return err (next_ty, indicator) = err.next_type_and_indicator() desc = lambda s: s.describe() front_str = f"Callable[[{', '.join(map(desc, self.fn.argument_checker))}], " responsable = WrappedFunction.find_location(self.fn.inner) err = err.with_frame( Frame(f"{front_str}{next_ty}]", (" " * len(front_str)) + indicator, declared=None, responsable=responsable)) return self.upper.wrap(err)
def get_type_hints(item, ctx: CreationContext, resolver=_default_resolver): try: # SEE: https://www.python.org/dev/peps/pep-0563/#id7 return resolver(item) except NameError as ne: org = WrappedFunction.find_original(item) if inspect.isclass(org): raise ctx.wrap( UntypyNameError( f"{ne}.\nType annotation inside of class '{qualname(org)}' could not be resolved." )) else: raise ctx.wrap( UntypyNameError( f"{ne}.\nType annotation of function '{qualname(org)}' could not be resolved." )) except Exception as e: # Try to find better cause in analyse raise analyse(item, ctx, e)
def location_of(fn): return WrappedFunction.find_location(fn)
def declared(self) -> Location: if self._declared is None: return WrappedFunction.find_location(self.inner) else: return self._declared
def describe(self) -> str: fn = WrappedFunction.find_original(self.inner) return f"{fn.__name__}" + str(self.signature)