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
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
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
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
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
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
def create_from(self, annotation: Any, ctx: CreationContext) -> Optional[TypeChecker]: if (type(annotation) is TupleType or type(annotation) is TupleTypeB) and annotation.__origin__ == tuple: args = annotation.__args__ if len(args) == 2 and args[1] == Ellipsis: checker = ctx.find_checker(args[0]) if checker is None: return None else: return VariadicTupleChecker(checker) inner = [] for arg in args: checker = ctx.find_checker(arg) if checker is None: return None else: inner.append(checker) return TupleChecker(inner) else: return None
def create_from(self, annotation: Any, ctx: CreationContext) -> Optional[TypeChecker]: if (type(annotation) is GenericAlias and annotation.__origin__ == list) or (type(annotation) is type(List[int]) and annotation.__origin__ == list): assert len(annotation.__args__) == 1 inner = ctx.find_checker(annotation.__args__[0]) if inner is None: return None return ListChecker(inner, ctx.declared_location()) else: return None
def create_from(self, annotation: Any, ctx: CreationContext) -> Optional[TypeChecker]: if type(annotation) is UnionType and len( annotation.__args__ ) == 2 and annotation.__args__[1] is type(None): checker = ctx.find_checker(annotation.__args__[0]) if checker is None: return None else: return OptionalChecker(checker) else: return None
def create_from(self, annotation: Any, ctx: CreationContext) -> Optional[TypeChecker]: if type(annotation) is str: eval_args = [annotation, globals()] local = ctx.eval_context() if local is not None: eval_args.append(local) def resolver(eval_args): return eval(*eval_args) annotation = get_type_hints(eval_args, ctx, resolver=resolver) return ctx.find_checker(annotation)
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
def create_from(self, annotation: Any, ctx: CreationContext) -> Optional[TypeChecker]: if type(annotation) is UnionType: inner = [] for arg in annotation.__args__: checker = ctx.find_checker(arg) if checker is None: return None else: inner.append(checker) return UnionChecker(inner, ctx) else: return None
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
def create_from(self, annotation: Any, ctx: CreationContext) -> Optional[TypeChecker]: if (type(annotation) is TupleType or type(annotation) is TupleTypeB ) and annotation.__origin__ == tuple: inner = [] for arg in annotation.__args__: checker = ctx.find_checker(arg) if checker is None: return None else: inner.append(checker) return TupleChecker(inner) else: return None
def create_from(self, annotation: Any, ctx: CreationContext) -> Optional[TypeChecker]: # TODO: Support other typevar features if type(annotation) is TypeVar: (found, replacement_annotation) = ctx.resolve_typevar(annotation) if found: inner = ctx.find_checker(replacement_annotation) if inner is not None: return BoundTypeVar(inner, annotation) else: return None else: return UnboundTypeVar(annotation) elif hasattr(annotation, '__args__') and hasattr(annotation.__origin__, '__mro__') and typing.Generic in annotation.__origin__.__mro__: return GenericProtocolChecker(annotation, ctx) else: return None
def WrappedGenericAlias(alias, ctx: CreationContext): typevars = dict(zip(alias.__origin__.__parameters__, alias.__args__)) for key, value in typevars.items(): _checktypevar(key, value) ctx = ctx.with_typevars(typevars) # This WrappedType must also be a generic alias. # So it can be as Typennotation tname = [] for t in typevars: a = ctx.find_checker(typevars[t]) if a is None: tname.append(str(typevars[t])) else: tname.append(a.describe()) wt = WrappedType(alias.__origin__, ctx, name=f"{alias.__origin__.__name__}[" + (', '.join(tname)) + "]") wt.__origin__ = alias.__origin__ wt.__args__ = alias.__args__ return wt
def create_from(self, annotation: Any, ctx: CreationContext) -> Optional[TypeChecker]: t = type(annotation) if (t in [SequenceTypeA, SequenceTypeB] and annotation.__origin__ in [Sequence, ABCSequence]) or \ annotation in [Sequence, ABCSequence]: # no args version try: args = annotation.__args__ except AttributeError: args = [] inner = [] elemChecker = None if len(args) == 0: sf = SimpleFactory() inner = [sf.create_from(list, ctx), sf.create_from(tuple, ctx), sf.create_from(str, ctx)] elif len(args) == 1: elemChecker = ctx.find_checker(args[0]) if elemChecker is None: return None inner = [ListChecker(elemChecker, ctx.declared_location()), VariadicTupleChecker(elemChecker)] return SequenceChecker(inner, ctx, elemChecker) else: return None
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