diff --git a/jedi/parser/__init__.py b/jedi/parser/__init__.py index b933ad5b..6e2ad210 100644 --- a/jedi/parser/__init__.py +++ b/jedi/parser/__init__.py @@ -101,16 +101,17 @@ class Parser(object): self.module.used_names[tok_name] = set([simple]) self.module.temp_used_names = [] - def _parse_dot_name(self, pre_used_token=None): + def _parse_dotted_name(self, pre_used_token=None): """ The dot name parser parses a name, variable or function and returns their names. + Just used for parsing imports. :return: tuple of Name, next_token """ - def append(el): - names.append(el) - self.module.temp_used_names.append(el[0]) + def append(tok): + names.append(pr.NamePart(self.module, tok.string, None, tok.start_pos)) + self.module.temp_used_names.append(tok.string) names = [] tok = next(self._gen) if pre_used_token is None else pre_used_token @@ -118,26 +119,23 @@ class Parser(object): if tok.type != tokenize.NAME and tok.string != '*': return None, tok - first_pos = tok.start_pos - append((tok.string, first_pos)) + append(tok) while True: - end_pos = tok.end_pos tok = next(self._gen) if tok.string != '.': break tok = next(self._gen) if tok.type != tokenize.NAME: break - append((tok.string, tok.start_pos)) + append(tok) - n = pr.Name(self.module, names, first_pos, end_pos) if names else None - return n, tok + return names, tok def _parse_name(self, pre_used_token=None): tok = next(self._gen) if pre_used_token is None else pre_used_token if tok.type != tokenize.NAME: - return None - return pr.NamePart(self.module, tok.string, None, tok.start_pos) + return None, tok + return pr.NamePart(self.module, tok.string, None, tok.start_pos), next(self._gen) def _parse_import_list(self): """ @@ -167,13 +165,13 @@ class Parser(object): tok = next(self._gen) if brackets and tok.type == tokenize.NEWLINE: tok = next(self._gen) - i, tok = self._parse_dot_name(tok) - if not i: + names, tok = self._parse_dotted_name(tok) + if not names: defunct = True - name2 = None + alias = None if tok.string == 'as': - name2, tok = self._parse_dot_name() - imports.append((i, name2, defunct)) + alias, tok = self._parse_name() + imports.append((names, alias, defunct)) while tok.string not in continue_kw: tok = next(self._gen) if not (tok.string == "," or brackets and tok.type == tokenize.NEWLINE): @@ -230,9 +228,8 @@ class Parser(object): if tok.type != tokenize.NAME: return None - fname = self._parse_name(tok) + fname, tok = self._parse_name(tok) - tok = next(self._gen) if tok.string != '(': return None params = self._parse_parentheses(is_class=False) @@ -269,10 +266,9 @@ class Parser(object): cname.start_pos[0], tokenize.tok_name[cname.type], cname.string) return None - cname = self._parse_name(cname) + cname, _next = self._parse_name(cname) superclasses = [] - _next = next(self._gen) if _next.string == '(': superclasses = self._parse_parentheses(is_class=True) _next = next(self._gen) @@ -346,7 +342,7 @@ class Parser(object): if tok.string == 'as': tok = next(self._gen) if tok.type == tokenize.NAME: - n, tok = self._parse_dot_name(self._gen.current) + n, tok = self._parse_name(self._gen.current) if n: set_vars.append(n) as_names.append(n) @@ -358,11 +354,6 @@ class Parser(object): elif in_lambda_param and tok.string == ':': in_lambda_param = False elif tok.type == tokenize.NAME and not is_kw: - n, tok = self._parse_dot_name(self._gen.current) - # removed last entry, because we add Name - tok_list.pop() - if n: - tok_list.append(n) continue elif tok.string in opening_brackets: level += 1 @@ -464,10 +455,10 @@ class Parser(object): # import stuff elif tok_str == 'import': imports = self._parse_import_list() - for count, (m, alias, defunct) in enumerate(imports): + for count, (names, alias, defunct) in enumerate(imports): e = (alias or m or self._gen.previous).end_pos end_pos = self._gen.previous.end_pos if count + 1 == len(imports) else e - i = pr.Import(self.module, first_pos, end_pos, m, + i = pr.Import(self.module, first_pos, end_pos, names, alias, defunct=defunct) self._check_user_stmt(i) self._scope.add_import(i) @@ -486,26 +477,26 @@ class Parser(object): break relative_count += 1 # the from import - mod, tok = self._parse_dot_name(self._gen.current) + from_names, tok = self._parse_dotted_name(self._gen.current) tok_str = tok.string - if str(mod) == 'import' and relative_count: + if len(from_names) == 1 and str(from_names[0]) == 'import' and relative_count: self._gen.push_last_back() tok_str = 'import' - mod = None - if not mod and not relative_count or tok_str != "import": + names = [] + if not names and not relative_count or tok_str != "import": debug.warning("from: syntax error@%s", tok.start_pos[0]) defunct = True if tok_str != 'import': self._gen.push_last_back() names = self._parse_import_list() - for count, (name, alias, defunct2) in enumerate(names): - star = name is not None and unicode(name.names[0]) == '*' + for count, (names, alias, defunct2) in enumerate(names): + star = names and unicode(names) == '*' if star: - name = None - e = (alias or name or self._gen.previous).end_pos + names = [] + e = (alias or names and names[-1] or self._gen.previous).end_pos end_pos = self._gen.previous.end_pos if count + 1 == len(names) else e - i = pr.Import(self.module, first_pos, end_pos, name, - alias, mod, star, relative_count, + i = pr.Import(self.module, first_pos, end_pos, names, + alias, from_names, star, relative_count, defunct=defunct or defunct2) self._check_user_stmt(i) self._scope.add_import(i) @@ -540,7 +531,7 @@ class Parser(object): if command == 'except' and tok.string == ',': # the except statement defines a var # this is only true for python 2 - n, tok = self._parse_dot_name() + n, tok = self._parse_name() if n: n.parent = statement statement.as_names.append(n) diff --git a/jedi/parser/representation.py b/jedi/parser/representation.py index 38cbdf51..02ede1f3 100644 --- a/jedi/parser/representation.py +++ b/jedi/parser/representation.py @@ -768,25 +768,25 @@ class Import(Simple): :param start_pos: Position (line, column) of the Import. :type start_pos: tuple(int, int) - :param namespace: The import, can be empty if a star is given - :type namespace: Name + :param namespace_names: The import, can be empty if a star is given + :type namespace_names: Name :param alias: The alias of a namespace(valid in the current namespace). :type alias: Name - :param from_ns: Like the namespace, can be equally used. - :type from_ns: Name + :param from_names: Like the namespace, can be equally used. + :type from_names: Name :param star: If a star is used -> from time import *. :type star: bool :param defunct: An Import is valid or not. :type defunct: bool """ - def __init__(self, module, start_pos, end_pos, namespace, alias=None, - from_ns=None, star=False, relative_count=0, defunct=False): + def __init__(self, module, start_pos, end_pos, namespace_names, alias=None, + from_names=None, star=False, relative_count=0, defunct=False): super(Import, self).__init__(module, start_pos, end_pos) - self.namespace = namespace + self.namespace_names = namespace_names self.alias = alias - self.from_ns = from_ns - for n in namespace, alias, from_ns: + self.from_names = from_names + for n in namespace_names, alias, from_names: if n: n.parent = self.use_as_parent @@ -797,20 +797,19 @@ class Import(Simple): def get_code(self, new_line=True): # in case one of the names is None alias = self.alias or '' - namespace = self.namespace or '' - from_ns = self.from_ns or '' + namespace = '.'.join(self.namespace_names) if self.alias: - ns_str = "%s as %s" % (namespace, alias) + ns_str = "%s as %s" % ('.'.join(namespace), alias) else: - ns_str = unicode(namespace) + ns_str = namespace nl = '\n' if new_line else '' - if self.from_ns or self.relative_count: + if self.from_names or self.relative_count: if self.star: ns_str = '*' dots = '.' * self.relative_count - return "from %s%s import %s%s" % (dots, from_ns, ns_str, nl) + return "from %s%s import %s%s" % (dots, '.'.join(self.from_names), ns_str, nl) else: return "import %s%s" % (ns_str, nl) @@ -821,22 +820,19 @@ class Import(Simple): return [self] if self.alias: return [self.alias] - if len(self.namespace) > 1: - o = self.namespace - n = Name(self._sub_module, [(unicode(o.names[0]), o.start_pos)], - o.start_pos, o.end_pos, parent=o.parent) - return [n] + if len(self.namespace_names) > 1: + return self.namespace_names[0] else: - return [self.namespace] + return [self.namespace_names] def get_all_import_names(self): n = [] - if self.from_ns: - n += self.from_ns.names - if self.namespace: - n += self.namespace.names - if self.alias: - n += self.alias.names + if self.from_names: + n += self.from_names + if self.namespace_names: + n += self.namespace_names + if self.alias is not None: + n.append(self.alias) return n @property @@ -854,7 +850,7 @@ class Import(Simple): import foo.bar """ - return not self.alias and not self.from_ns and self.namespace is not None \ + return not self.alias and not self.from_names and self.namespace is not None \ and len(self.namespace.names) > 1