コード例 #1
0
ファイル: generator.py プロジェクト: CodeSteak/untypy
    def create_from(self, annotation: Any,
                    ctx: CreationContext) -> Optional[TypeChecker]:
        if type(annotation) in [
                GeneratorTypeA, GeneratorTypeB
        ] and annotation.__origin__ == collections.abc.Generator:
            if len(annotation.__args__) != 3:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"Expected 3 type arguments for Generator."))

            (yield_checker, send_checker, return_checker) = list(
                map(lambda a: ctx.find_checker(a), annotation.__args__))

            if yield_checker is None:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"The Yield Annotation of the Generator could not be resolved."
                    ))
            if send_checker is None:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"The Send Annotation of the Generator could not be resolved."
                    ))
            if return_checker is None:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"The Return Annotation of the Generator could not be resolved."
                    ))

            return GeneratorChecker(yield_checker, send_checker,
                                    return_checker)
        else:
            return None
コード例 #2
0
def _checktypevar(typevar: TypeVar, bound: Any) -> None:
    # See https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance
    if len(typevar.__constraints__) > 0:
        if typevar.__covariant__ == True:
            if not issubclass(bound, typevar.__constraints__):
                raise UntypyAttributeError(
                    f"Violation in TypeVar {typevar}: {bound} must be a covariant of one of type {typevar.__constraints__}."
                )
        elif typevar.__contravariant__ == True:
            found = False
            for c in typevar.__constraints__:
                if issubclass(c, bound):
                    found = True
            if not found:
                raise UntypyAttributeError(
                    f"Violation in TypeVar {typevar}: {bound} must be a contravariant of one of type {typevar.__constraints__}."
                )
        else:
            if not bound in typevar.__constraints__:
                raise UntypyAttributeError(
                    f"Violation in TypeVar {typevar}: {bound} must be a exactly of one of type {typevar.__constraints__}."
                    f"\nYou may want to use TypeVar(..., bound=...) instead.")
    elif typevar.__bound__ is not None:
        if not type(typevar.__bound__) is type:
            raise UntypyAttributeError(
                f"Bound {bound} of TypeVar {typevar} must be a type.")
        if not issubclass(bound, typevar.__bound__):
            raise UntypyAttributeError(
                f"Violation in TypeVar {typevar}: {bound} must be a subclass of {typevar.__bound__}."
            )
コード例 #3
0
ファイル: interface.py プロジェクト: CodeSteak/untypy
    def create_from(self, annotation: Any, ctx: CreationContext) -> Optional[TypeChecker]:
        if hasattr(annotation, '__origin__') and hasattr(annotation,
                                                         '__args__') and annotation.__origin__ in InterfaceMapping:
            (protocol,) = InterfaceMapping[annotation.__origin__]
            bindings = protocol.__parameters__  # args of Generic super class
            origin = annotation.__origin__

            inner_checkers = []
            for param in annotation.__args__:
                ch = ctx.find_checker(param)
                if ch is None:
                    raise UntypyAttributeError(f"Could not resolve annotation {param} inside of {annotation}")
                inner_checkers.append(ch)
            if len(inner_checkers) != len(bindings):
                raise UntypyAttributeError(f"Expected {len(bindings)} type arguments inside of {annotation}")

            name = f"{origin.__name__}[" + (', '.join(map(lambda t: t.describe(), inner_checkers))) + "]"

            bindings = dict(zip(bindings, annotation.__args__))
            ctx.with_typevars(bindings)
            template = WrappedType(protocol, ctx.with_typevars(bindings), name=name, implementation_template=origin,
                                   declared=ctx.declared_location())
            return InterfaceChecker(origin, template, name)
        else:
            return None
コード例 #4
0
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"))
コード例 #5
0
    def create_from(self, annotation: Any,
                    ctx: CreationContext) -> Optional[TypeChecker]:
        if annotation in InterfaceMapping:
            # Assume Any if no parameters are given
            (protocol, ) = InterfaceMapping[annotation]
            bindings = protocol.__parameters__

            if len(bindings) == 0:
                raise AssertionError(
                    f"This is a BUG. {annotation} has no generic params.")

            # handle Python inconsistency
            if hasattr(annotation, '__class_getitem__'):
                return self.create_from(
                    annotation.__class_getitem__(*([Any] * len(bindings))),
                    ctx)
            elif hasattr(annotation, '__getitem__'):
                return self.create_from(
                    annotation.__getitem__(*([Any] * len(bindings))), ctx)

        elif hasattr(annotation, '__origin__') and hasattr(
                annotation,
                '__args__') and annotation.__origin__ in InterfaceMapping:
            (protocol, ) = InterfaceMapping[annotation.__origin__]
            bindings = protocol.__parameters__  # args of Generic super class
            origin = annotation.__origin__

            inner_checkers = []
            for param in annotation.__args__:
                ch = ctx.find_checker(param)
                if ch is None:
                    raise UntypyAttributeError(
                        f"Could not resolve annotation {param} inside of {annotation}"
                    )
                inner_checkers.append(ch)
            if len(inner_checkers) != len(bindings):
                raise UntypyAttributeError(
                    f"Expected {len(bindings)} type arguments inside of {annotation}"
                )

            name = f"{origin.__name__}[" + (', '.join(
                map(lambda t: t.describe(), inner_checkers))) + "]"

            bindings = dict(zip(bindings, annotation.__args__))
            ctx = ctx.with_typevars(bindings)
            if type(origin) == type:
                template = WrappedType(protocol,
                                       ctx.with_typevars(bindings),
                                       name=name,
                                       implementation_template=origin,
                                       declared=ctx.declared_location())
                return InterfaceChecker(origin, template, name)
            else:
                # type(origin) == collection.abc.ABCMeta
                return ProtocolChecker(protocol, ctx, altname=name)
        else:
            return None
コード例 #6
0
ファイル: typedfunction.py プロジェクト: CodeSteak/untypy
    def __init__(self, inner: Callable, ctx: CreationContext):
        self.inner = inner
        self.signature = inspect.signature(inner)

        # SEE: https://www.python.org/dev/peps/pep-0563/#id7
        annotations = typing.get_type_hints(inner, include_extras=True)

        checkers = {}
        checked_keys = list(self.signature.parameters)

        # Remove self and cls from checking
        if len(checked_keys) > 0 and checked_keys[0] in self.special_args:
            checkers[checked_keys[0]] = SelfChecker()
            checked_keys = checked_keys[1:]

        for key in checked_keys:
            if self.signature.parameters[
                    key].annotation is inspect.Parameter.empty:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"Missing annotation for argument '{key}' of function {inner.__name__}\n"
                        "Partial annotation are not supported."))
            annotation = annotations[key]
            checker = ctx.find_checker(annotation)
            if checker is None:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"\n\tUnsupported type annotation: {annotation}\n"
                        f"\tin argument '{key}'"))
            else:
                checkers[key] = checker

        if inner.__name__ in self.method_name_ignore_return:
            checkers['return'] = SelfChecker()
        else:
            if not 'return' in annotations:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"Missing annotation for return value of function {inner.__name__}\n"
                        "Partial annotation are not supported. Use 'None' or 'NoReturn'"
                        "for specifying no return value."))
            annotation = annotations['return']
            return_checker = ctx.find_checker(annotation)
            if return_checker is None:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"\n\tUnsupported type annotation: {annotation}\n"
                        f"\tin return"))

            checkers['return'] = return_checker

        self.fc = None
        if hasattr(self.inner, "__fc"):
            self.fc = getattr(self.inner, "__fc")
        self.checkers = checkers
コード例 #7
0
    def create_from(self, annotation: Any, ctx: CreationContext) -> Optional[TypeChecker]:
        if type(annotation) in [IteratorTypeA, IteratorTypeB] and annotation.__origin__ == collections.abc.Iterator:
            if len(annotation.__args__) != 1:
                raise ctx.wrap(UntypyAttributeError(f"Expected 1 type arguments for iterator."))

            inner = ctx.find_checker(annotation.__args__[0])
            if inner is None:
                raise ctx.wrap(UntypyAttributeError(f"The inner type of the iterator could not be resolved."))
            return IteratorChecker(inner)
        else:
            return None
コード例 #8
0
    def __init__(self, annotated, inner: TypeChecker, metadata: Iterator,
                 ctx: CreationContext):
        self.annotated = annotated
        self.inner = inner

        meta = []
        info = []
        for m in metadata:
            if callable(m):
                meta.append(AnnotatedCheckerCallable(self, m))
            elif isinstance(m, str):
                info.append(m)
            elif hasattr(m, '__contains__'):
                meta.append(AnnotatedCheckerContainer(self, m))
            else:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"Unsupported metadata '{repr(m)}' in '{repr(self.annotated)}'.\n"
                        f"Only callables or objects providing __contains__ are allowed."
                    ))
        self.meta = meta
        self.info = info
        if len(info) == 1:
            self.name = info[0]
            self.info = []
        else:
            self.name = None
コード例 #9
0
    def build(self):
        def wrapper(*args, **kwargs):
            # first is this fn
            caller = sys._getframe(1)
            (args, kwargs, bindings) = self.wrap_arguments(lambda n: ArgumentExecutionContext(self, caller, n), args,
                                                           kwargs)
            ret = self.inner(*args, **kwargs)
            ret = self.wrap_return(ret, bindings, ReturnExecutionContext(self))
            return ret

        if inspect.iscoroutine(self.inner):
            raise UntypyAttributeError("Async functions are currently not supported.")
        else:
            w = wrapper

        setattr(w, '__wrapped__', self.inner)
        setattr(w, '__name__', self.inner.__name__)
        setattr(w, '__signature__', self.signature)
        setattr(w, '__wf', self)

        # Copy useful attributes
        # This is need for the detection of abstract classes
        for attr in ['__isabstractmethod__']:
            if hasattr(self.inner, attr):
                setattr(w, attr, getattr(self.inner, attr))

        return w
コード例 #10
0
    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)
コード例 #11
0
    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)
コード例 #12
0
def find_signature(member, ctx: CreationContext):
    signature = inspect.signature(member)

    checkers = {}
    for key in signature.parameters:
        if key == 'self':
            checkers[key] = SelfChecker()
        else:
            param = signature.parameters[key]
            if param.annotation is inspect.Parameter.empty:
                checkers[key] = AnyChecker()
                continue

            checker = ctx.find_checker(param.annotation)
            if checker is None:
                checkers[key] = AnyChecker()
                continue
            checkers[key] = checker

    if signature.return_annotation is inspect.Parameter.empty:
        checkers['return'] = AnyChecker()
    else:
        return_checker = ctx.find_checker(signature.return_annotation)
        if return_checker is None:
            raise ctx.wrap(
                UntypyAttributeError(
                    f"\n\tUnsupported Type Annotation: {signature.return_annotation}\n"
                    f"for Return Value of function {member.__name__}\n"))
        checkers['return'] = return_checker
    return signature, checkers
コード例 #13
0
    def checkers(self) -> Dict[str, TypeChecker]:
        if self._checkers is not None:
            return self._checkers

        annotations = get_type_hints(self.inner, self.ctx)

        checkers = {}
        checked_keys = list(self.signature.parameters)

        # Remove self and cls from checking
        if len(checked_keys) > 0 and checked_keys[0] in self.special_args:
            checkers[checked_keys[0]] = SelfChecker()
            checked_keys = checked_keys[1:]

        for key in checked_keys:
            if self.signature.parameters[key].annotation is inspect.Parameter.empty:
                raise self.ctx.wrap(
                    UntypyAttributeError(f"Missing annotation for argument '{key}' of function {self.inner.__name__}\n"
                                         "Partial annotation are not supported."))
            annotation = annotations[key]
            checker = self.ctx.find_checker(annotation)
            if checker is None:
                raise self.ctx.wrap(UntypyAttributeError(f"\n\tUnsupported type annotation: {annotation}\n"
                                                         f"\tin argument '{key}'"))
            else:
                checkers[key] = checker

        if self.inner.__name__ in self.method_name_ignore_return:
            checkers['return'] = SelfChecker()
        else:
            if not 'return' in annotations:
                annotation = None
            else:
                annotation = annotations['return']
            return_checker = self.ctx.find_checker(annotation)
            if return_checker is None:
                raise self.ctx.wrap(UntypyAttributeError(f"\n\tUnsupported type annotation: {annotation}\n"
                                                         f"\tin return"))

            checkers['return'] = return_checker

        self._checkers = checkers
        return checkers
コード例 #14
0
    def __init__(self, annotation: Union[CallableTypeOne, CallableTypeTwo],
                 ctx: CreationContext):
        arguments_ty = annotation.__args__[:-1]
        return_ty = annotation.__args__[-1]

        return_checker = ctx.find_checker(return_ty)
        if return_checker is None:
            raise ctx.wrap(
                UntypyAttributeError(
                    f"Return Type Annotation not found. {return_ty}"))

        argument_checker = []
        for arg in arguments_ty:
            checker = ctx.find_checker(arg)
            if checker is None:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"Argument Type Annotation not found. {arg}"))
            argument_checker.append(checker)

        self.return_checker = return_checker
        self.argument_checker = argument_checker
コード例 #15
0
    def create_from(self, annotation: Any,
                    ctx: CreationContext) -> Optional[TypeChecker]:
        if hasattr(annotation, '__metadata__') and hasattr(
                annotation, '__origin__'):
            inner = ctx.find_checker(annotation.__origin__)
            if inner is None:
                raise ctx.wrap(
                    UntypyAttributeError(f"Could not resolve annotation "
                                         f"'{repr(annotation.__origin__)}' "
                                         f"inside of '{annotation}'."))

            return AnnotatedChecker(annotation, inner, annotation.__metadata__,
                                    ctx)
        else:
            return None
コード例 #16
0
ファイル: protocol.py プロジェクト: CodeSteak/untypy
def _find_bound_typevars(clas: type) -> (type, Dict[TypeVar, Any]):
    if not hasattr(clas, '__args__') or not hasattr(clas, '__origin__'):
        return (clas, dict())
    if not hasattr(clas.__origin__, '__parameters__'):
        return (clas, dict())

    keys = clas.__origin__.__parameters__
    values = clas.__args__

    if len(keys) != len(values):
        raise UntypyAttributeError(
            f"Some unbound Parameters in {clas.__name__}. "
            f"keys={keys} do not match values={values}.", [
                Location(file=inspect.getfile(clas),
                         line_no=inspect.getsourcelines(clas)[1],
                         source_line="".join(inspect.getsourcelines(clas)[0]))
            ])
    return (clas.__origin__, dict(zip(keys, values)))
コード例 #17
0
    def build(self):
        fn = self.inner
        name = fn.__name__

        def wrapper_cls(*args, **kwargs):
            caller = sys._getframe(1)
            (args, kwargs, bindings) = self.wrap_arguments(
                lambda n: ArgumentExecutionContext(
                    wrapper_cls, caller, n, declared=self.declared()), args,
                kwargs)
            ret = fn(*args, **kwargs)
            return self.wrap_return(ret, bindings,
                                    ReturnExecutionContext(self))

        def wrapper_self(me, *args, **kwargs):
            if name == '__init__':
                me.__return_ctx = None
                me.__inner = self.create_fn()
            caller = sys._getframe(1)
            (args, kwargs, bindings) = self.wrap_arguments(
                lambda n: ArgumentExecutionContext(
                    wrapper_self, caller, n, declared=self.declared()),
                (me.__inner, *args), kwargs)
            ret = fn(*args, **kwargs)
            if me.__return_ctx is None:
                return self.wrap_return(ret, bindings,
                                        ReturnExecutionContext(self))
            else:
                return self.wrap_return(ret, bindings, me.__return_ctx)

        if inspect.iscoroutine(self.inner):
            raise UntypyAttributeError(
                "Async Functions are currently not supported.")
        else:
            if 'self' in self.checker:
                w = wrapper_self
            else:
                w = wrapper_cls

        setattr(w, '__wrapped__', fn)
        setattr(w, '__name__', fn.__name__)
        setattr(w, '__signature__', self.signature)
        setattr(w, '__wf', self)
        return w
コード例 #18
0
 def __init__(self, inner: list[TypeChecker], ctx: CreationContext):
     # especially Protocols must be checked in a specific order.
     self.inner = sorted(inner, key=lambda t: -t.base_type_priority())
     dups = dict()
     for checker in inner:
         for base_type in checker.base_type():
             if base_type in dups:
                 raise ctx.wrap(
                     UntypyAttributeError(
                         f"{checker.describe()} is in conflict with "
                         f"{dups[base_type].describe()} "
                         f"in {self.describe()}. "
                         f"Types must be distinguishable inside one Union."
                         f"\nNote: Only one Protocol is allowed inside one Union. "
                         f"Classes could implement multiple Protocols by accident."
                         f"\nNote: Multiple Callables or Generics inside one Union are also unsupported."
                     ))
             else:
                 dups[base_type] = checker
コード例 #19
0
    def get_checker(self):
        if self._checker:
            return self._checker

        ctx = DefaultCreationContext(
            typevars=dict(),
            declared_location=Location.from_code(self.declared),
            checkedpkgprefixes=self.cfg.checkedprefixes)
        try:
            annotation = self.annotation()
        except NameError as ne:
            raise ctx.wrap(
                UntypyNameError(
                    f"{ne}.\nType annotation could not be resolved."))

        checker = ctx.find_checker(annotation)
        if checker is None:
            raise ctx.wrap(
                UntypyAttributeError(
                    f"\n\tUnsupported type annotation: {self.annotation}\n"))
        self._checker = checker
        return checker
コード例 #20
0
ファイル: typedfunction.py プロジェクト: CodeSteak/untypy
    def build(self):
        def wrapper(*args, **kwargs):
            # first is this fn
            caller = sys._getframe(1)
            (args, kwargs, bindings) = self.wrap_arguments(
                lambda n: ArgumentExecutionContext(self, caller, n), args,
                kwargs)
            ret = self.inner(*args, **kwargs)
            ret = self.wrap_return(ret, bindings, ReturnExecutionContext(self))
            return ret

        if inspect.iscoroutine(self.inner):
            raise UntypyAttributeError(
                "Async functions are currently not supported.")
        else:
            w = wrapper

        setattr(w, '__wrapped__', self.inner)
        setattr(w, '__name__', self.inner.__name__)
        setattr(w, '__signature__', self.signature)
        setattr(w, '__wf', self)
        return w
コード例 #21
0
ファイル: protocol.py プロジェクト: CodeSteak/untypy
def get_proto_members(
    proto: type, ctx: CreationContext
) -> Dict[str, Tuple[inspect.Signature, dict[str, TypeChecker],
                     FunctionCondition]]:
    blacklist = [
        '__init__', '__class__', '__delattr__', '__dict__', '__dir__',
        '__doc__', '__getattribute__', '__getattr__', '__init_subclass__',
        '__new__', '__setattr__', '__subclasshook__', '__weakref__',
        '__abstractmethods__', '__class_getitem__'
    ]

    member_dict = {}
    for [name, member] in inspect.getmembers(proto):
        if name in blacklist:
            continue

        if inspect.isfunction(member):
            member = WrappedFunction.find_original(member)
            signature = inspect.signature(member)
            annotations = typing.get_type_hints(member, include_extras=True)
            checkers = {}
            for key in signature.parameters:
                if key == 'self':
                    checkers[key] = SelfChecker()
                else:
                    param = signature.parameters[key]
                    if param.annotation is inspect.Parameter.empty:
                        raise ctx.wrap(
                            UntypyAttributeError(
                                f"Missing annotation for argument '{key}' of function {member.__name__} "
                                f"in protocol {proto.__name__}\n"))

                    checker = ctx.find_checker(annotations[key])
                    if checker is None:
                        raise ctx.wrap(
                            UntypyAttributeError(
                                f"\n\tUnsupported type annotation: {param.annotation}\n"
                                f"for argument '{key}' of function {member.__name__} "
                                f"in protocol {proto.__name__}.\n"))
                    checkers[key] = checker

            if signature.return_annotation is inspect.Parameter.empty:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"Missing annotation for return value of function {member.__name__} "
                        f"in protocol {proto.__name__}. Use 'None' if there is no return value.\n"
                    ))
            return_annotation = annotations['return']
            if return_annotation is proto:  # Self as Return Type would led to endless recursion
                return_checker = SimpleInstanceOfChecker(proto, None)
            else:
                return_checker = ctx.find_checker(return_annotation)

            if return_checker is None:
                raise ctx.wrap(
                    UntypyAttributeError(
                        f"\n\tUnsupported type annotation: {signature.return_annotation}\n"
                        f"for return value of function {member.__name__} "
                        f"in protocol-like {proto.__name__}.\n"))
            fc = None
            if hasattr(member, '__fc'):
                fc = getattr(member, '__fc')
            checkers['return'] = return_checker
            member_dict[name] = (signature, checkers, fc)
    return member_dict