def _get_match_location(self, node, name=None): loc = source.Location(node.lineno, node.col_offset) if not name: return loc if isinstance(node, (self._ast.Import, self._ast.ImportFrom)): # Search for imported module names m = re.search("[ ,]" + name + r"\b", self.source.line(node.lineno)) if m is not None: c, _ = m.span() return source.Location(node.lineno, c + 1) elif isinstance(node, self._ast.Attribute): attr_loc, _ = self.source.get_attr_location(name, loc) return attr_loc return loc
def _get_match_location(self, node, name=None): loc = source.Location(node.lineno, node.col_offset) if not name: return loc if isinstance(node, (self._ast.Import, self._ast.ImportFrom)): # Search for imported module names text = self.source.line(node.lineno) c = text.find(" " + name) if c == -1: c = text.find("," + name) if c != -1: return source.Location(node.lineno, c + 1) elif isinstance(node, self._ast.Attribute): attr_loc, _ = self.source.get_attr_location(name, loc) return attr_loc return loc
def test_get_offset_multibyte(self): # With single-byte characters src = source.Code(""" # coding=utf-8 line1 # a line2 """, [], _FakeTrace, "") self.assertEqual(src.get_offset(source.Location(4, 3)), 41) # With a multibyte character the byte offset should change src = source.Code(""" # coding=utf-8 line1 # ツ line2 """, [], _FakeTrace, "") self.assertEqual(src.get_offset(source.Location(4, 3)), 43)
def process_import(self, node): """Common code for Import and ImportFrom.""" for alias, (loc, (op, symbol, data)) in zip(node.names, self.match(node)): # If an import is aliased, match() returns only the symbol/loc of # the alias, whereas the indexer also needs access to the unaliased # name in order to reference the imported module. defn = None # type: Optional[Definition] if alias.asname: defn = self.add_local_def( node, name=symbol, target=alias.name, data=data) defloc = self.locs[defn.id].pop() self.locs[defn.id].append(DefLocation(defloc.def_id, loc)) # Shift symbol/loc back to the unaliased name. symbol = alias.name m = re.search("[ ,]" + symbol + r"\b", self.source.line(loc.line)) assert m is not None c, _ = m.span() loc = source.Location(loc.line, c + 1) try: [imported] = _unwrap(data) except ValueError: continue # Unresolved import. if op == "STORE_NAME": # for |import x.y as z| or |from x import y as z| we want {z: x.y} self.add_local_ref(node, name=symbol, data=data, location=loc) if not isinstance(imported, abstract.Module): # Make the from-imported symbol available in the current namespace. remote = Remote(imported.module, name=symbol, resolved=True) if defn: self.aliases[defn.id] = remote self.current_env[symbol] = remote self.typemap[remote.id] = [imported] continue if defn: remote = Remote(imported.full_name, IMPORT_FILE_MARKER, resolved=True) self.aliases[defn.id] = remote self.modules[defn.id] = imported.full_name else: self.modules[self.scope_id() + "." + symbol] = imported.full_name elif op == "IMPORT_NAME": # |import x.y| puts both {x: x} and {x.y: x.y} in modules self.add_local_ref(node, name=symbol, data=data, location=loc) # TODO(slebedev): Reference every import path component. # For example here # # from foo.bar import boo # import foo.bar.boo # # we should reference both foo and foo.bar (in addition to foo.bar.boo). for mod in module_utils.get_all_prefixes(symbol): self.modules[self.scope_id() + "." + mod] = mod
def make_def(self, node, **kwargs): """Make a definition from a node.""" if isinstance(node, self._ast.Name): t = node_utils.typename(node.ctx) elif isinstance(node, self._ast.arg): t = "Param" else: t = node_utils.typename(node) args = { "name": node_utils.get_name(node, self._ast), "scope": self.scope_id(), "typ": t, "data": None, "target": None, "doc": None, } args.update(kwargs) defn = Definition(**args) line, col = self._get_location(node, args) assert line is not None defloc = DefLocation(defn.id, source.Location(line, col)) return (defn, defloc)
def process_import(self, node): """Common code for Import and ImportFrom.""" # Only record modules that pytype has resolved in self.modules def is_resolved(data): return data and isinstance(data[0], abstract.Module) def add_import_ref(name, data, loc): self.add_global_ref(node, name=name, data=data, location=loc, typ="Import") for loc, (op, symbol, data) in self.match(node): d = self.add_local_def(node, name=symbol) defloc = self.locs[d.id][-1] # tweak the definition location slightly line, _ = loc text = self.source.line(line) c = text.find("import ") new_loc = source.Location(line, c) if c > -1 else loc self.locs[d.id][-1] = DefLocation(defloc.def_id, new_loc) if not is_resolved(_unwrap(data)): continue elif op == "STORE_NAME": # for |import x.y as z| or |from x import y as z| we want {z: x.y} self.modules[d.id] = _unwrap(data)[0].full_name add_import_ref(name=symbol, data=data, loc=loc) elif op == "IMPORT_NAME": # |import x.y| puts both {x: x} and {x.y: x.y} in modules add_import_ref(name=symbol, data=data, loc=loc) for mod in module_utils.get_all_prefixes(symbol): # TODO(mdemello): Create references for every element. self.modules[d.scope + "." + mod] = mod
def test_one_line(self): src = source.Code("foo.bar", [], _FakeTrace, "") self.assertEqual( src.get_attr_location("foo.bar", source.Location(1, 0)), (source.Location(1, 4), 3))
def test_find_first_text(self): src = source.Code("line1\nline2\nline3", [], _FakeTrace, "") self.assertEqual(src.find_first_text(2, 5, "line"), source.Location(2, 0)) self.assertIsNone(src.find_first_text(2, 5, "duck"))
def test_get_offset(self): src = source.Code("line1\nline2", [], _FakeTrace, "") self.assertEqual(src.get_offset(source.Location(2, 3)), 9)
def test_not_found(self): src = source.Code("foo.bar", [], _FakeTrace, "") self.assertEqual( src.get_attr_location("foo.baz", source.Location(1, 0)), (source.Location(1, 0), 7))
def test_dot_attr(self): src = source.Code("foo\n.bar", [], _FakeTrace, "") self.assertEqual( src.get_attr_location("foo.bar", source.Location(1, 0)), (source.Location(2, 1), 3))
def get_location(node): # TODO(mdemello): The column offset for nodes like "class A" needs to be # adjusted to the start of the symbol. return source.Location(node.lineno, node.col_offset)