def visit_instance(self, t: Instance) -> str: s = t.type.fullname() or t.type.name() or None if s is None: return '<???>' if s in reverse_builtin_aliases: s = reverse_builtin_aliases[s] mod_obj = split_target(self.graph, s) assert mod_obj mod, obj = mod_obj # If a class is imported into the current module, rewrite the reference # to point to the current module. This helps the annotation tool avoid # inserting redundant imports when a type has been reexported. if self.module: parts = obj.split( '.') # need to split the object part if it is a nested class tree = self.graph[self.module].tree if tree and parts[0] in tree.names: mod = self.module if (mod, obj) == ('builtins', 'tuple'): mod, obj = 'typing', 'Tuple[' + t.args[0].accept(self) + ', ...]' elif t.args != []: obj += '[{}]'.format(self.list_str(t.args)) if mod_obj == ('builtins', 'unicode'): return 'Text' elif mod == 'builtins': return obj else: delim = '.' if '.' not in obj else ':' return mod + delim + obj
def load_final_static(self, fullname: str, typ: RType, line: int, error_name: Optional[str] = None) -> Value: split_name = split_target(self.graph, fullname) assert split_name is not None module, name = split_name return self.builder.load_static_checked( typ, name, module, line=line, error_msg='value for final name "{}" was not set'.format(error_name))
def load_final_static(self, fullname: str, typ: RType, line: int, error_name: Optional[str] = None) -> Value: if error_name is None: error_name = fullname ok_block, error_block = BasicBlock(), BasicBlock() split_name = split_target(self.graph, fullname) assert split_name is not None value = self.add(LoadStatic(typ, split_name[1], split_name[0], line=line)) self.add(Branch(value, error_block, ok_block, Branch.IS_ERROR, rare=True)) self.activate_block(error_block) self.add(RaiseStandardError(RaiseStandardError.VALUE_ERROR, 'value for final name "{}" was not set'.format(error_name), line)) self.add(Unreachable()) self.activate_block(ok_block) return value
def find_node(self, key: str) -> Tuple[str, str, FuncDef]: """From a target name, return module/target names and the func def.""" # TODO: Also return OverloadedFuncDef -- currently these are ignored. graph = self.fgmanager.graph target = split_target(graph, key) if not target: raise SuggestionFailure("Cannot find module for %s" % (key, )) modname, tail = target tree = self.ensure_loaded(graph[modname]) # N.B. This is reimplemented from update's lookup_target # basically just to produce better error messages. names = tree.names # type: SymbolTable # Look through any classes components = tail.split('.') for i, component in enumerate(components[:-1]): if component not in names: raise SuggestionFailure( "Unknown class %s.%s" % (modname, '.'.join(components[:i + 1]))) node = names[component].node # type: Optional[SymbolNode] if not isinstance(node, TypeInfo): raise SuggestionFailure( "Object %s.%s is not a class" % (modname, '.'.join(components[:i + 1]))) names = node.names # Look for the actual function/method funcname = components[-1] if funcname not in names: raise SuggestionFailure( "Unknown %s %s" % ("method" if len(components) > 1 else "function", key)) node = names[funcname].node if isinstance(node, Decorator): node = self.extract_from_decorator(node) if not node: raise SuggestionFailure( "Object %s is a decorator we can't handle" % key) if not isinstance(node, FuncDef): raise SuggestionFailure("Object %s is not a function" % key) return (modname, tail, node)
def find_node(self, key: str) -> Tuple[str, str, FuncDef]: """From a target name, return module/target names and the func def. The 'key' argument can be in one of two formats: * As the function full name, e.g., package.module.Cls.method * As the function location as file and line separated by column, e.g., path/to/file.py:42 """ # TODO: Also return OverloadedFuncDef -- currently these are ignored. node = None # type: Optional[SymbolNode] if ':' in key: if key.count(':') > 1: raise SuggestionFailure( 'Malformed location for function: {}. Must be either' ' package.module.Class.method or path/to/file.py:line'. format(key)) file, line = key.split(':') if not line.isdigit(): raise SuggestionFailure( 'Line number must be a number. Got {}'.format(line)) line_number = int(line) modname, node = self.find_node_by_file_and_line(file, line_number) tail = node.fullname()[len(modname) + 1:] # add one to account for '.' else: target = split_target(self.fgmanager.graph, key) if not target: raise SuggestionFailure("Cannot find module for %s" % (key, )) modname, tail = target node = self.find_node_by_module_and_name(modname, tail) if isinstance(node, Decorator): node = self.extract_from_decorator(node) if not node: raise SuggestionFailure( "Object %s is a decorator we can't handle" % key) if not isinstance(node, FuncDef): raise SuggestionFailure("Object %s is not a function" % key) return modname, tail, node
def lookup_target(manager: BuildManager, target: str) -> Tuple[List[FineGrainedDeferredNode], Optional[TypeInfo]]: """Look up a target by fully-qualified name. The first item in the return tuple is a list of deferred nodes that needs to be reprocessed. If the target represents a TypeInfo corresponding to a protocol, return it as a second item in the return tuple, otherwise None. """ def not_found() -> None: manager.log_fine_grained( "Can't find matching target for %s (stale dependency?)" % target) modules = manager.modules items = split_target(modules, target) if items is None: not_found() # Stale dependency return [], None module, rest = items if rest: components = rest.split('.') else: components = [] node = modules[module] # type: Optional[SymbolNode] file = None # type: Optional[MypyFile] active_class = None for c in components: if isinstance(node, TypeInfo): active_class = node if isinstance(node, MypyFile): file = node if (not isinstance(node, (MypyFile, TypeInfo)) or c not in node.names): not_found() # Stale dependency return [], None # Don't reprocess plugin generated targets. They should get # stripped and regenerated when the containing target is # reprocessed. if node.names[c].plugin_generated: return [], None node = node.names[c].node if isinstance(node, TypeInfo): # A ClassDef target covers the body of the class and everything defined # within it. To get the body we include the entire surrounding target, # typically a module top-level, since we don't support processing class # bodies as separate entitites for simplicity. assert file is not None if node.fullname != target: # This is a reference to a different TypeInfo, likely due to a stale dependency. # Processing them would spell trouble -- for example, we could be refreshing # a deserialized TypeInfo with missing attributes. not_found() return [], None result = [FineGrainedDeferredNode(file, None)] stale_info = None # type: Optional[TypeInfo] if node.is_protocol: stale_info = node for name, symnode in node.names.items(): node = symnode.node if isinstance(node, FuncDef): method, _ = lookup_target(manager, target + '.' + name) result.extend(method) return result, stale_info if isinstance(node, Decorator): # Decorator targets actually refer to the function definition only. node = node.func if not isinstance(node, (FuncDef, MypyFile, OverloadedFuncDef)): # The target can't be refreshed. It's possible that the target was # changed to another type and we have a stale dependency pointing to it. not_found() return [], None if node.fullname != target: # Stale reference points to something unexpected. We shouldn't process since the # context will be wrong and it could be a partially initialized deserialized node. not_found() return [], None return [FineGrainedDeferredNode(node, active_class)], None