def to_pytd_literal(self): """Make a pytd node from Literal[self.value].""" if self.value is None: return pytd.NamedType("None") if self.type in _STRING_TYPES: val = self.repr_str() elif self.type == "float": raise ParseError(f"Invalid type `float` in Literal[{self.value}].") else: val = self.value return pytd.Literal(val)
def visit_AnnAssign(self, node): self.convert_node_annotations(node) name = node.target.id typ = node.annotation val = self.convert_node(node.value) msg = f"Default value for {name}: {typ.name} can only be '...', got {val}" if typ.name and pytd_utils.MatchesFullName(typ, _FINAL_IDS): if isinstance(node.value, types.Pyval): # to_pytd_literal raises an exception if the value is a float, but # checking upfront allows us to generate a nicer error message. if isinstance(node.value.value, float): msg = (f"Default value for {name}: Final can only be '...' or a " f"legal Literal parameter, got {val}") else: typ = node.value.to_pytd_literal() val = pytd.AnythingType() elif isinstance(val, pytd.NamedType): typ = pytd.Literal(val) val = pytd.AnythingType() if val and not types.is_any(val): raise ParseError(msg) return pytd.Constant(name, typ, val)
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, typing_overlay.NoReturn)): return pytd.NothingType() elif isinstance(v, abstract.TypeParameterInstance): if v.module in self._scopes: return self._typeparam_to_def(node, v.param, v.param.name) elif v.instance.get_instance_type_parameter(v.full_name).bindings: # The type parameter was initialized. Set the view to None, since we # don't include v.instance in the view. return pytd_utils.JoinTypes( self.value_to_pytd_type(node, p, seen, None) for p in v.instance.get_instance_type_parameter(v.full_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) elif v.param.bound: return self.value_instance_to_pytd_type( node, v.param.bound, None, seen, view) else: return pytd.AnythingType() elif isinstance(v, typing_overlay.TypeVar): return pytd.NamedType("builtins.type") elif isinstance(v, dataclass_overlay.FieldInstance): if not v.default: return pytd.AnythingType() return pytd_utils.JoinTypes( self.value_to_pytd_type(node, d, seen, view) for d in v.default.data) elif isinstance(v, abstract.FUNCTION_TYPES): try: signatures = abstract_utils.get_signatures(v) except NotImplementedError: return pytd.NamedType("typing.Callable") if len(signatures) == 1: val = self.signature_to_callable(signatures[0]) if not isinstance( v, abstract.PYTD_FUNCTION_TYPES) or not val.formal: # 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, (abstract.ClassMethod, abstract.StaticMethod)): return self.value_to_pytd_type(node, v.method, seen, view) elif isinstance(v, (special_builtins.IsInstance, special_builtins.ClassMethodCallable)): return pytd.NamedType("typing.Callable") elif isinstance(v, class_mixin.Class): param = self.value_instance_to_pytd_type(node, v, None, seen, view) return pytd.GenericType(base_type=pytd.NamedType("builtins.type"), parameters=(param, )) elif isinstance(v, abstract.Module): return pytd.NamedType("builtins.module") elif (self._output_mode >= Converter.OutputMode.LITERAL and isinstance(v, abstract.ConcreteValue) and isinstance(v.pyval, (int, str, bytes))): # LITERAL mode is used only for pretty-printing, so we just stringify the # inner value rather than properly converting it. return pytd.Literal(repr(v.pyval)) elif isinstance(v, abstract.SimpleValue): if v.cls: ret = self.value_instance_to_pytd_type(node, v.cls, v, seen=seen, view=view) ret.Visit( visitors.FillInLocalPointers( {"builtins": self.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 Any for %s", v.name) return pytd.AnythingType() elif isinstance(v, abstract.Union): opts = [] for o in v.options: # NOTE: Guarding printing of type parameters behind _detailed until # round-tripping is working properly. if self._detailed and isinstance(o, abstract.TypeParameter): opt = self._typeparam_to_def(node, o, o.name) else: opt = self.value_to_pytd_type(node, o, seen, view) opts.append(opt) return pytd.UnionType(tuple(opts)) elif isinstance(v, special_builtins.SuperInstance): return pytd.NamedType("builtins.super") elif isinstance(v, 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 # unless self._detailed is set. if self._detailed: return pytd.NamedType("typing.TypeVar") else: return pytd.AnythingType() elif isinstance(v, abstract.Unsolvable): return pytd.AnythingType() elif isinstance(v, abstract.Unknown): return pytd.NamedType(v.class_name) elif isinstance(v, abstract.BuildClass): return pytd.NamedType("typing.Callable") else: raise NotImplementedError(v.__class__.__name__)
def value_instance_to_pytd_type(self, node, v, instance, seen, view): """Get the PyTD type an instance of this object would have. Args: node: The node. v: The object. instance: The instance. seen: Already seen instances. view: A Variable -> binding map. Returns: A PyTD type. """ if isinstance(v, abstract.Union): return pytd.UnionType( tuple( self.value_instance_to_pytd_type(node, t, instance, seen, view) for t in v.options)) elif isinstance(v, abstract.AnnotationContainer): return self.value_instance_to_pytd_type(node, v.base_cls, instance, seen, view) elif isinstance(v, abstract.LiteralClass): if not v.value: # TODO(b/173742489): Remove this workaround once we support literal # enums. return pytd.AnythingType() if isinstance(v.value.pyval, (str, bytes)): # Strings are stored as strings of their representations, prefix and # quotes and all. value = repr(v.value.pyval) elif isinstance(v.value.pyval, bool): # True and False are stored as pytd constants. value = self.vm.lookup_builtin(f"builtins.{v.value.pyval}") else: # Ints are stored as their literal values. Note that Literal[None] or a # nested literal will never appear here, since we simplified it to None # or unnested it, respectively, in typing_overlay. Literal[<enum>] does # not appear here yet because it is unsupported. assert isinstance(v.value.pyval, int), v.value.pyval value = v.value.pyval return pytd.Literal(value) elif isinstance(v, class_mixin.Class): if not self._detailed and v.official_name is None: return pytd.AnythingType() if seen is None: # We make the set immutable to ensure that the seen instances for # different parameter values don't interfere with one another. seen = frozenset() if instance in seen: # We have a circular dependency in our types (e.g., lst[0] == lst). Stop # descending into the type parameters. type_params = () else: type_params = tuple(t.name for t in v.template) if instance is not None: seen |= {instance} type_arguments = self._value_to_parameter_types( node, v, instance, type_params, seen, view) base = pytd_utils.NamedTypeWithModule(v.official_name or v.name, v.module) if self._is_tuple(v, instance): homogeneous = False elif v.full_name == "typing.Callable": homogeneous = not isinstance(v, abstract.CallableClass) else: homogeneous = len(type_arguments) == 1 return pytd_utils.MakeClassOrContainerType(base, type_arguments, homogeneous) elif isinstance(v, abstract.TypeParameter): # We generate the full definition because, if this type parameter is # imported, we will need the definition in order to declare it later. return self._typeparam_to_def(node, v, v.name) elif isinstance(v, typing_overlay.NoReturn): return pytd.NothingType() else: log.info("Using Any for instance of %s", v.name) return pytd.AnythingType()
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, typing_overlay.NoReturn)): return pytd.NothingType() elif isinstance(v, abstract.TypeParameterInstance): if (v.module in self._scopes or v.instance is abstract_utils.DUMMY_CONTAINER): return self._typeparam_to_def(node, v.param, v.param.name) elif v.instance.get_instance_type_parameter(v.full_name).bindings: # The type parameter was initialized. Set the view to None, since we # don't include v.instance in the view. return pytd_utils.JoinTypes( self.value_to_pytd_type(node, p, seen, None) for p in v.instance.get_instance_type_parameter(v.full_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) elif v.param.bound: return self.value_instance_to_pytd_type( node, v.param.bound, None, seen, view) else: return pytd.AnythingType() elif isinstance(v, typing_overlay.TypeVar): return pytd.NamedType("builtins.type") elif isinstance(v, dataclass_overlay.FieldInstance): if not v.default: return pytd.AnythingType() return pytd_utils.JoinTypes( self.value_to_pytd_type(node, d, seen, view) for d in v.default.data) elif isinstance(v, attr_overlay.AttribInstance): ret = self.value_to_pytd_type(node, v.typ, seen, view) md = metadata.to_pytd(v.to_metadata()) return pytd.Annotated(ret, ("'pytype_metadata'", md)) elif isinstance(v, special_builtins.PropertyInstance): return pytd.NamedType("builtins.property") elif isinstance(v, typed_dict.TypedDict): return pytd.NamedType(v.props.name) elif isinstance(v, abstract.FUNCTION_TYPES): try: signatures = function.get_signatures(v) except NotImplementedError: return pytd.NamedType("typing.Callable") if len(signatures) == 1: val = self.signature_to_callable(signatures[0]) if not isinstance( v, abstract.PYTD_FUNCTION_TYPES) or not val.formal: # 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, (abstract.ClassMethod, abstract.StaticMethod)): return self.value_to_pytd_type(node, v.method, seen, view) elif isinstance(v, (special_builtins.IsInstance, 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("builtins.type"), parameters=(param, )) elif isinstance(v, abstract.Module): return pytd.Alias(v.name, pytd.Module(v.name, module_name=v.full_name)) elif (self._output_mode >= Converter.OutputMode.LITERAL and isinstance(v, abstract.ConcreteValue) and isinstance(v.pyval, (int, str, bytes))): # LITERAL mode is used only for pretty-printing, so we just stringify the # inner value rather than properly converting it. return pytd.Literal(repr(v.pyval)) elif isinstance(v, abstract.SimpleValue): ret = self.value_instance_to_pytd_type(node, v.cls, v, seen=seen, view=view) ret.Visit( visitors.FillInLocalPointers( {"builtins": self.ctx.loader.builtins})) return ret elif isinstance(v, abstract.Union): return pytd_utils.JoinTypes( self.value_to_pytd_type(node, o, seen, view) for o in v.options) elif isinstance(v, special_builtins.SuperInstance): return pytd.NamedType("builtins.super") elif isinstance(v, 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 # unless self._detailed is set. if self._detailed: return pytd.NamedType("typing.TypeVar") else: return pytd.AnythingType() elif isinstance(v, abstract.Unsolvable): return pytd.AnythingType() elif isinstance(v, abstract.Unknown): return pytd.NamedType(v.class_name) elif isinstance(v, abstract.BuildClass): return pytd.NamedType("typing.Callable") elif isinstance(v, abstract.FinalAnnotation): param = self.value_to_pytd_type(node, v.annotation, seen, view) return pytd.GenericType(base_type=pytd.NamedType("typing.Final"), parameters=(param, )) else: raise NotImplementedError(v.__class__.__name__)