diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 5003848c..1d95e3f7 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -355,6 +355,9 @@ def _remove_statements(evaluator, stmt, name): check_instance = stmt.instance stmt = stmt.var + pep0484types = pep0484.find_type_from_comment_hint(evaluator, stmt) + if pep0484types: + return pep0484types types |= evaluator.eval_statement(stmt, seek_name=name) if check_instance is not None: diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 08588d71..519a217d 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -28,6 +28,7 @@ from jedi.common import unite from jedi.evaluate import compiled from jedi import debug from jedi import _compatibility +import re def _evaluate_for_annotation(evaluator, annotation): @@ -136,3 +137,30 @@ def get_types_for_typing_module(evaluator, typ, node): result = evaluator.execute_evaluated(factory, compiled_classname, args) return result + + +def find_type_from_comment_hint(evaluator, stmt): + try: + stmtpos = stmt.parent.children.index(stmt) + except ValueError: + return [] + try: + next_sibling = stmt.parent.children[stmtpos + 1] + except IndexError: + return [] + if not isinstance(next_sibling, tree.Whitespace): + return [] + comment = next_sibling.get_pre_comment() + if comment is None: + return [] + match = re.match(r"\s*type:\s*([^#]*)", comment) + if not match: + return [] + start_pos = (next_sibling.start_pos[0], + next_sibling.start_pos[1] - len(comment)) + annotation = tree.String( + tree.zero_position_modifier, + repr(str(match.group(1).strip())), + start_pos) + annotation.parent = stmt.parent + return _evaluate_for_annotation(evaluator, annotation) diff --git a/jedi/parser/tree.py b/jedi/parser/tree.py index 89c37841..3f8549d4 100644 --- a/jedi/parser/tree.py +++ b/jedi/parser/tree.py @@ -280,6 +280,15 @@ class Leaf(Base): def nodes_to_execute(self, last_added=False): return [] + def get_pre_comment(self): + """ + returns comment before this leaf, excluding #, or None if no comment + """ + match = re.match(r"\s*#(.*)$", self.prefix) + if match: + return match.group(1) + return None + @utf8_repr def __repr__(self): return "<%s: %s>" % (type(self).__name__, self.value) diff --git a/test/completion/pep0484.py b/test/completion/pep0484.py index fc08460f..1990cda0 100644 --- a/test/completion/pep0484.py +++ b/test/completion/pep0484.py @@ -157,3 +157,27 @@ Y = int def just_because_we_can(x: "flo" + "at"): #? float() x + +# python >= 2.6 + +x = 3 # type: str +#? str() +x + +y = 3 # type: str but I write more +#? int() +y + +z = 3 # type: str # I comment more +#? str() +z + +class BB: pass + +def test(a, b): + a = a # type: BB + c = a # type: str + #? BB() + a + #? str() + c