def value_to_pytd_type(self, node, v, seen, view): """Get a PyTD type representing this object, as seen at a node. Args: node: The node from which we want to observe this object. v: The object. seen: The set of values seen before while computing the type. view: A Variable -> binding map. Returns: A PyTD type. """ if isinstance(v, (abstract.Empty, abstract.Nothing)): return pytd.NothingType() elif isinstance(v, abstract.TypeParameterInstance): if v.instance.type_parameters[v.name].bindings: # The type parameter was initialized. return pytd_utils.JoinTypes( self.value_to_pytd_type(node, p, seen, view) for p in v.instance.type_parameters[v.name].data) elif v.param.constraints: return pytd_utils.JoinTypes( self.value_instance_to_pytd_type(node, p, None, seen, view) for p in v.param.constraints) else: return pytd.AnythingType() elif isinstance(v, typing.TypeVar): return pytd.NamedType("__builtin__.type") elif isinstance( v, (abstract.InterpreterFunction, abstract.BoundInterpreterFunction)): sig, = abstract.get_signatures(v) return self.value_instance_to_pytd_type( node, self.signature_to_callable(sig, v.vm), None, seen, view) elif isinstance(v, (abstract.PyTDFunction, abstract.BoundPyTDFunction)): signatures = abstract.get_signatures(v) if len(signatures) == 1: val = self.signature_to_callable(signatures[0], v.vm) if not v.vm.annotations_util.get_type_parameters(val): # This is a workaround to make sure we don't put unexpected type # parameters in call traces. return self.value_instance_to_pytd_type( node, val, None, seen, view) return pytd.NamedType("typing.Callable") elif isinstance( v, (special_builtins.IsInstance, abstract.ClassMethod, abstract.StaticMethod, special_builtins.ClassMethodCallable)): return pytd.NamedType("typing.Callable") elif isinstance(v, abstract.Class): param = self.value_instance_to_pytd_type(node, v, None, seen, view) return pytd.GenericType( base_type=pytd.NamedType("__builtin__.type"), parameters=(param, )) elif isinstance(v, abstract.Module): return pytd.NamedType("__builtin__.module") elif isinstance(v, abstract.SimpleAbstractValue): if v.cls: classvalues = self._get_values(node, v.cls, view) cls_types = [] for cls in classvalues: cls_types.append( self.value_instance_to_pytd_type(node, cls, v, seen=seen, view=view)) ret = pytd_utils.JoinTypes(cls_types) ret.Visit( visitors.FillInLocalPointers( {"__builtin__": v.vm.loader.builtins})) return ret else: # We don't know this type's __class__, so return AnythingType to # indicate that we don't know anything about what this is. # This happens e.g. for locals / globals, which are returned from the # code in class declarations. log.info("Using ? for %s", v.name) return pytd.AnythingType() elif isinstance(v, abstract.Union): return pytd.UnionType( tuple( self.value_to_pytd_type(node, o, seen, view) for o in v.options)) elif isinstance(v, special_builtins.SuperInstance): return pytd.NamedType("__builtin__.super") elif isinstance(v, (abstract.Unsolvable, abstract.TypeParameter)): # Arguably, the type of a type parameter is NamedType("typing.TypeVar"), # but pytype doesn't know how to handle that, so let's just go with Any. return pytd.AnythingType() elif isinstance(v, abstract.Unknown): return pytd.NamedType(v.class_name) else: raise NotImplementedError(v.__class__.__name__)
def _match_type_against_type(self, left, other_type, subst, node, view): """Checks whether a type is compatible with a (formal) type. Args: left: A type. other_type: A formal type. E.g. abstract.Class or abstract.Union. subst: The current type parameter assignment. node: The current CFG node. view: The current mapping of Variable to Value. Returns: A new type parameter assignment if the matching succeeded, None otherwise. """ if (isinstance(left, abstract.Empty) and isinstance(other_type, abstract.Empty)): return subst elif isinstance(left, abstract.AMBIGUOUS_OR_EMPTY): params = self.vm.annotations_util.get_type_parameters(other_type) if isinstance(left, abstract.Empty): value = self.vm.convert.empty else: value = self.vm.convert.unsolvable return self._mutate_type_parameters(params, value, subst, node) elif isinstance(left, abstract.Class): if (other_type.full_name == "__builtin__.type" and isinstance(other_type, abstract.ParameterizedClass)): other_type = other_type.type_parameters[abstract.T] return self._instantiate_and_match(left, other_type, subst, node, view) elif (other_type.full_name == "typing.Callable" and isinstance(other_type, abstract.ParameterizedClass)): other_type = other_type.type_parameters[abstract.RET] return self._instantiate_and_match(left, other_type, subst, node, view) elif other_type.full_name in [ "__builtin__.type", "__builtin__.object", "typing.Callable" ]: return subst elif left.cls: return self._match_instance_against_type( left, other_type, subst, node, view) elif isinstance(left, abstract.Module): if other_type.full_name in [ "__builtin__.module", "__builtin__.object", "types.ModuleType" ]: return subst elif isinstance(left, (abstract.Function, abstract.BoundFunction)): if other_type.full_name == "typing.Callable": if not isinstance(other_type, abstract.ParameterizedClass): # The callable has no parameters, so any function matches it. return subst if isinstance(left, abstract.NativeFunction): # If we could get the class on which 'left' is defined (perhaps by # using bound_class?), we could get the argument and return types # from the underlying PyTDFunction, but we wouldn't get much value # out of that additional matching, since most NativeFunction objects # are magic methods like __getitem__ which aren't likely to be passed # as function arguments. return subst signatures = abstract.get_signatures(left) for sig in signatures: new_subst = self._match_signature_against_callable( sig, other_type, subst, node, view) if new_subst is not None: return new_subst return None elif isinstance(left, abstract.SimpleAbstractValue): return self._match_instance_against_type(left, other_type, subst, node, view) elif isinstance(left, special_builtins.SuperInstance): return self._match_class_and_instance_against_type( left.super_cls, left.super_obj, other_type, subst, node, view) elif isinstance(left, abstract.ClassMethod): if other_type.full_name in [ "__builtin__.classmethod", "__builtin__.object" ]: return subst elif isinstance(left, abstract.StaticMethod): if other_type.full_name in [ "__builtin__.staticmethod", "__builtin__.object" ]: return subst elif isinstance(left, abstract.Union): for o in left.options: new_subst = self._match_type_against_type( o, other_type, subst, node, view) if new_subst is not None: return new_subst elif isinstance(left, abstract.TypeParameterInstance): return self._instantiate_and_match(left.param, other_type, subst, node, view) else: raise NotImplementedError( "Matching not implemented for %s against %s" % (type(left), type(other_type)))