From f5a31ad78e66b8d04dae94c7368b3aab8679302e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Dec 2015 00:03:08 +0100 Subject: [PATCH 01/26] first try at the typing library --- jedi/evaluate/iterable.py | 14 ++++++++++- jedi/evaluate/pep0484.py | 44 ++++++++++++++++++++++++++++++--- jedi/evaluate/representation.py | 18 ++++++++++++++ test/completion/pep0484.py | 23 +++++++++++++++++ 4 files changed, 95 insertions(+), 4 deletions(-) diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index b11b95a1..5b0871fd 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -29,6 +29,7 @@ from jedi.evaluate import compiled from jedi.evaluate import helpers from jedi.evaluate.cache import CachedMetaClass, memoize_default from jedi.evaluate import analysis +from jedi.evaluate import pep0484 class IterableWrapper(tree.Base): @@ -560,12 +561,23 @@ def py__iter__types(evaluator, types, node=None): def py__getitem__(evaluator, types, index, node): + from jedi.evaluate.representation import Class result = set() # Index handling. if isinstance(index, (compiled.CompiledObject, Slice)): index = index.obj + # special case: PEP0484 typing module, see + # https://github.com/davidhalter/jedi/issues/663 + for typ in list(types): + if isinstance(typ, Class): + replacementclass = \ + pep0484.get_typing_replacement_class(evaluator, typ) + if replacementclass: + types.remove(typ) + result |= replacementclass.py__getitem__(index) + if type(index) not in (float, int, str, unicode, slice): # If the index is not clearly defined, we have to get all the # possiblities. @@ -588,7 +600,7 @@ def py__getitem__(evaluator, types, index, node): except IndexError: result |= py__iter__types(evaluator, set([typ])) except KeyError: - # Must be a dict. Lists don't raise IndexErrors. + # Must be a dict. Lists don't raise KeyErrors. result |= typ.dict_values() return result diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 4eec2fed..761886a4 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -20,9 +20,10 @@ x support for type hint comments `# type: (int, str) -> int`. See comment from from itertools import chain -from jedi.parser import Parser, load_grammar, ParseError +from jedi.parser import Parser, load_grammar, ParseError, tree, ParserWithRecovery from jedi.evaluate.cache import memoize_default -from jedi.evaluate.compiled import CompiledObject +from jedi.evaluate import compiled +from textwrap import dedent from jedi import debug @@ -30,7 +31,7 @@ def _evaluate_for_annotation(evaluator, annotation): if annotation is not None: definitions = set() for definition in evaluator.eval_element(annotation): - if (isinstance(definition, CompiledObject) and + if (isinstance(definition, compiled.CompiledObject) and isinstance(definition.obj, str)): try: p = Parser(load_grammar(), definition.obj, start='eval_input') @@ -60,3 +61,40 @@ def follow_param(evaluator, param): def find_return_types(evaluator, func): annotation = func.py__annotations__().get("return", None) return _evaluate_for_annotation(evaluator, annotation) + + +# TODO: Memoize +def get_typing_replacement_module(): + """ + The idea is to return our jedi replacement for the PEP-0484 typing module + as discussed at https://github.com/davidhalter/jedi/issues/663 + """ + + code = dedent(""" + from collections import abc + + class MakeSequence: + def __getitem__(self, indextype): + class Sequence(abc.Sequence): + def __getitem__(self) -> indextype: + pass + return Sequence + """) + p = ParserWithRecovery(load_grammar(), code) + return p.module + + +def get_typing_replacement_class(evaluator, typ): + if not typ.base.get_parent_until(tree.Module).name.value == "typing": + return None + # we assume that any class using [] in a module called + # "typing" with a name for which we have a replacement + # should be replaced by that class. This is not 100% + # airtight but I don't have a better idea to check that it's + # actually the PEP-0484 typing module and not some other + typing = get_typing_replacement_module() + types = evaluator.find_types(typing, "Make" + typ.name.value) + if not types: + return None + else: + return list(types)[0] diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index c460efd2..35359dbd 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -469,6 +469,24 @@ class Class(use_metaclass(CachedMetaClass, Wrapper)): def py__class__(self): return compiled.create(self._evaluator, type) + def py__getitem__(self, index): + instances = self.py__call__(()) + assert len(instances) == 1 + instance = list(instances)[0] + try: + method = instance.get_subscope_by_name('__getitem__') + except KeyError: + debug.warning('No __getitem__, cannot access the array.') + return set() + else: + # it feels like this should be handled somewhere else, + # not sure where though + if isinstance(index, type(self)): + index_obj = index + else: + index_obj = compiled.create(self._evaluator, index) + return instance._evaluator.execute_evaluated(method, index_obj) + @property def params(self): return self.get_subscope_by_name('__init__').params diff --git a/test/completion/pep0484.py b/test/completion/pep0484.py index fc08460f..ba9044bc 100644 --- a/test/completion/pep0484.py +++ b/test/completion/pep0484.py @@ -157,3 +157,26 @@ Y = int def just_because_we_can(x: "flo" + "at"): #? float() x + +import typing +def we_can_has_sequence( + p: typing.Sequence[int], + q: typing.Sequence[B], + r: "typing.Sequence[int]", + s: typing.Sequence["int"]): + #? ["count"] + p.c + #? int() + p[1] + #? ["count"] + q.c + #? B() + q[1] + #? ["count"] + r.c + #? int() + r[1] + #? ["count"] + s.c + #? int() + s[1] From 52cc721f450eaa438dee6a28006a19a6b3420b14 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Dec 2015 16:39:17 +0100 Subject: [PATCH 02/26] made typing classes inheritable; added MutableSequence and List --- jedi/evaluate/iterable.py | 12 ++++++---- jedi/evaluate/jedi_typing.py | 25 ++++++++++++++++++++ jedi/evaluate/pep0484.py | 42 +++++++++++++++++++-------------- jedi/evaluate/representation.py | 18 -------------- test/completion/pep0484.py | 14 ++++++++++- 5 files changed, 70 insertions(+), 41 deletions(-) create mode 100644 jedi/evaluate/jedi_typing.py diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index 5b0871fd..d6f0da0f 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -572,11 +572,15 @@ def py__getitem__(evaluator, types, index, node): # https://github.com/davidhalter/jedi/issues/663 for typ in list(types): if isinstance(typ, Class): - replacementclass = \ - pep0484.get_typing_replacement_class(evaluator, typ) - if replacementclass: + typing_module_types = \ + pep0484.get_types_for_typing_module(evaluator, typ, index) + if typing_module_types is not None: types.remove(typ) - result |= replacementclass.py__getitem__(index) + result |= typing_module_types + + if not types: + # all consumed by special cases + return result if type(index) not in (float, int, str, unicode, slice): # If the index is not clearly defined, we have to get all the diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py new file mode 100644 index 00000000..9538624a --- /dev/null +++ b/jedi/evaluate/jedi_typing.py @@ -0,0 +1,25 @@ +""" +This module is not intended to be used in jedi, rather it will be fed to the +jedi-parser to replace classes in the typing module +""" + +from collections import abc + + +def factory(typing_name, indextype): + class Sequence(abc.Sequence): + def __getitem__(self) -> indextype: + pass + + class MutableSequence(Sequence, abc.MutableSequence): + pass + + class List(MutableSequence, list): + pass + + dct = { + "Sequence": Sequence, + "MutableSequence": MutableSequence, + "List": List, + } + return dct[typing_name] diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 761886a4..d3fb46e0 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -20,10 +20,11 @@ x support for type hint comments `# type: (int, str) -> int`. See comment from from itertools import chain -from jedi.parser import Parser, load_grammar, ParseError, tree, ParserWithRecovery +import os +from jedi.parser import \ + Parser, load_grammar, ParseError, tree, ParserWithRecovery from jedi.evaluate.cache import memoize_default from jedi.evaluate import compiled -from textwrap import dedent from jedi import debug @@ -34,7 +35,8 @@ def _evaluate_for_annotation(evaluator, annotation): if (isinstance(definition, compiled.CompiledObject) and isinstance(definition.obj, str)): try: - p = Parser(load_grammar(), definition.obj, start='eval_input') + p = Parser( + load_grammar(), definition.obj, start='eval_input') element = p.get_parsed_node() except ParseError: debug.warning('Annotation not parsed: %s' % definition.obj) @@ -70,21 +72,15 @@ def get_typing_replacement_module(): as discussed at https://github.com/davidhalter/jedi/issues/663 """ - code = dedent(""" - from collections import abc - - class MakeSequence: - def __getitem__(self, indextype): - class Sequence(abc.Sequence): - def __getitem__(self) -> indextype: - pass - return Sequence - """) + typing_path = os.path.abspath(os.path.join(__file__, "../jedi_typing.py")) + with open(typing_path) as f: + code = f.read() p = ParserWithRecovery(load_grammar(), code) return p.module -def get_typing_replacement_class(evaluator, typ): +def get_types_for_typing_module(evaluator, typ, index): + from jedi.evaluate.representation import Class if not typ.base.get_parent_until(tree.Module).name.value == "typing": return None # we assume that any class using [] in a module called @@ -93,8 +89,18 @@ def get_typing_replacement_class(evaluator, typ): # airtight but I don't have a better idea to check that it's # actually the PEP-0484 typing module and not some other typing = get_typing_replacement_module() - types = evaluator.find_types(typing, "Make" + typ.name.value) - if not types: - return None + factories = evaluator.find_types(typing, "factory") + assert len(factories) == 1 + factory = list(factories)[0] + assert factory + compiled_classname = compiled.create(evaluator, typ.name.value) + if isinstance(index, Class): + index_obj = index else: - return list(types)[0] + index_obj = compiled.create(evaluator, index) + result = \ + evaluator.execute_evaluated(factory, compiled_classname, index_obj) + if result: + return result + else: + return None diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 35359dbd..c460efd2 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -469,24 +469,6 @@ class Class(use_metaclass(CachedMetaClass, Wrapper)): def py__class__(self): return compiled.create(self._evaluator, type) - def py__getitem__(self, index): - instances = self.py__call__(()) - assert len(instances) == 1 - instance = list(instances)[0] - try: - method = instance.get_subscope_by_name('__getitem__') - except KeyError: - debug.warning('No __getitem__, cannot access the array.') - return set() - else: - # it feels like this should be handled somewhere else, - # not sure where though - if isinstance(index, type(self)): - index_obj = index - else: - index_obj = compiled.create(self._evaluator, index) - return instance._evaluator.execute_evaluated(method, index_obj) - @property def params(self): return self.get_subscope_by_name('__init__').params diff --git a/test/completion/pep0484.py b/test/completion/pep0484.py index ba9044bc..75742846 100644 --- a/test/completion/pep0484.py +++ b/test/completion/pep0484.py @@ -163,7 +163,9 @@ def we_can_has_sequence( p: typing.Sequence[int], q: typing.Sequence[B], r: "typing.Sequence[int]", - s: typing.Sequence["int"]): + s: typing.Sequence["int"], + t: typing.MutableSequence[dict], + u: typing.List[float]): #? ["count"] p.c #? int() @@ -180,3 +182,13 @@ def we_can_has_sequence( s.c #? int() s[1] + #? [] + s.a + #? ["append"] + t.a + #? dict() + t[1] + #? ["append"] + u.a + #? float() + u[1] From cc6bd7d1611b73486143a8e6eb75c1d725d64db0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Dec 2015 18:47:08 +0100 Subject: [PATCH 03/26] rework so that it also works withouty pep0484 type hints in jedi_typing.py --- jedi/evaluate/__init__.py | 3 +- jedi/evaluate/iterable.py | 61 +++++++++++++++++-------------- jedi/evaluate/jedi_typing.py | 5 +-- jedi/evaluate/pep0484.py | 69 +++++++++++++++++++++--------------- 4 files changed, 79 insertions(+), 59 deletions(-) diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 29d73818..3c5b1fdf 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -391,8 +391,7 @@ class Evaluator(object): new_types = set() if trailer_op == '[': - for trailer_typ in iterable.create_index_types(self, node): - new_types |= iterable.py__getitem__(self, types, trailer_typ, trailer_op) + new_types |= iterable.py__getitem__(self, types, trailer) else: for typ in types: debug.dbg('eval_trailer: %s in scope %s', trailer, typ) diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index d6f0da0f..957f9784 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -560,20 +560,17 @@ def py__iter__types(evaluator, types, node=None): return unite(py__iter__(evaluator, types, node)) -def py__getitem__(evaluator, types, index, node): +def py__getitem__(evaluator, types, trailer): from jedi.evaluate.representation import Class result = set() - # Index handling. - if isinstance(index, (compiled.CompiledObject, Slice)): - index = index.obj # special case: PEP0484 typing module, see # https://github.com/davidhalter/jedi/issues/663 for typ in list(types): if isinstance(typ, Class): typing_module_types = \ - pep0484.get_types_for_typing_module(evaluator, typ, index) + pep0484.get_types_for_typing_module(evaluator, typ, trailer) if typing_module_types is not None: types.remove(typ) result |= typing_module_types @@ -582,30 +579,40 @@ def py__getitem__(evaluator, types, index, node): # all consumed by special cases return result - if type(index) not in (float, int, str, unicode, slice): - # If the index is not clearly defined, we have to get all the - # possiblities. - for typ in list(types): - if isinstance(typ, Array) and typ.type == 'dict': - types.remove(typ) - result |= typ.dict_values() - return result | py__iter__types(evaluator, types) + trailer_op, node, trailer_cl = trailer.children[:3] + assert trailer_op == "[" + if trailer_cl != "]": + debug.warning("No support for complex indices: %s" % trailer) + return result - for typ in types: - # The actual getitem call. - try: - getitem = typ.py__getitem__ - except AttributeError: - analysis.add(evaluator, 'type-error-not-subscriptable', node, - message="TypeError: '%s' object is not subscriptable" % typ) - else: + for index in create_index_types(evaluator, node): + if isinstance(index, (compiled.CompiledObject, Slice)): + index = index.obj + + if type(index) not in (float, int, str, unicode, slice): + # If the index is not clearly defined, we have to get all the + # possiblities. + for typ in list(types): + if isinstance(typ, Array) and typ.type == 'dict': + types.remove(typ) + result |= typ.dict_values() + return result | py__iter__types(evaluator, types) + + for typ in types: + # The actual getitem call. try: - result |= getitem(index) - except IndexError: - result |= py__iter__types(evaluator, set([typ])) - except KeyError: - # Must be a dict. Lists don't raise KeyErrors. - result |= typ.dict_values() + getitem = typ.py__getitem__ + except AttributeError: + analysis.add(evaluator, 'type-error-not-subscriptable', trailer_op, + message="TypeError: '%s' object is not subscriptable" % typ) + else: + try: + result |= getitem(index) + except IndexError: + result |= py__iter__types(evaluator, set([typ])) + except KeyError: + # Must be a dict. Lists don't raise KeyErrors. + result |= typ.dict_values() return result diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py index 9538624a..14c74570 100644 --- a/jedi/evaluate/jedi_typing.py +++ b/jedi/evaluate/jedi_typing.py @@ -8,10 +8,11 @@ from collections import abc def factory(typing_name, indextype): class Sequence(abc.Sequence): - def __getitem__(self) -> indextype: - pass + def __getitem__(self, index: int): + return indextype() class MutableSequence(Sequence, abc.MutableSequence): + def __setitem__(self, index: int, value: indextype): pass class List(MutableSequence, list): diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index d3fb46e0..771a7a14 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -21,8 +21,7 @@ x support for type hint comments `# type: (int, str) -> int`. See comment from from itertools import chain import os -from jedi.parser import \ - Parser, load_grammar, ParseError, tree, ParserWithRecovery +from jedi.parser import Parser, load_grammar, ParseError, ParserWithRecovery from jedi.evaluate.cache import memoize_default from jedi.evaluate import compiled from jedi import debug @@ -31,28 +30,34 @@ from jedi import debug def _evaluate_for_annotation(evaluator, annotation): if annotation is not None: definitions = set() + module = annotation.get_parent_until() for definition in evaluator.eval_element(annotation): - if (isinstance(definition, compiled.CompiledObject) and - isinstance(definition.obj, str)): - try: - p = Parser( - load_grammar(), definition.obj, start='eval_input') - element = p.get_parsed_node() - except ParseError: - debug.warning('Annotation not parsed: %s' % definition.obj) - else: - module = annotation.get_parent_until() - p.position_modifier.line = module.end_pos[0] - element.parent = module - definitions |= evaluator.eval_element(element) - else: - definitions.add(definition) + definitions |= \ + _fix_forward_reference(evaluator, definition, module) return list(chain.from_iterable( evaluator.execute(d) for d in definitions)) else: return [] +def _fix_forward_reference(evaluator, item, module): + if (isinstance(item, compiled.CompiledObject) and + isinstance(item.obj, str)): + try: + p = Parser( + load_grammar(), item.obj, start='eval_input') + element = p.get_parsed_node() + except ParseError: + debug.warning('Annotation not parsed: %s' % item.obj) + return set() + else: + p.position_modifier.line = module.end_pos[0] + element.parent = module + return evaluator.eval_element(element) + else: + return {item} + + @memoize_default(None, evaluator_is_first_arg=True) def follow_param(evaluator, param): annotation = param.annotation() @@ -66,7 +71,7 @@ def find_return_types(evaluator, func): # TODO: Memoize -def get_typing_replacement_module(): +def _get_typing_replacement_module(): """ The idea is to return our jedi replacement for the PEP-0484 typing module as discussed at https://github.com/davidhalter/jedi/issues/663 @@ -79,27 +84,35 @@ def get_typing_replacement_module(): return p.module -def get_types_for_typing_module(evaluator, typ, index): - from jedi.evaluate.representation import Class - if not typ.base.get_parent_until(tree.Module).name.value == "typing": +def get_types_for_typing_module(evaluator, typ, trailer): + if not typ.base.get_parent_until().name.value == "typing": return None # we assume that any class using [] in a module called # "typing" with a name for which we have a replacement # should be replaced by that class. This is not 100% # airtight but I don't have a better idea to check that it's # actually the PEP-0484 typing module and not some other - typing = get_typing_replacement_module() + indextypes = evaluator.eval_element(trailer.children[1]) + if not isinstance(indextypes, set): + indextypes = {indextypes} + + module = trailer.get_parent_until() + dereferencedindextypes = set() + for indextyp in indextypes: + dereferencedindextypes |= \ + _fix_forward_reference(evaluator, indextyp, module) + + typing = _get_typing_replacement_module() factories = evaluator.find_types(typing, "factory") assert len(factories) == 1 factory = list(factories)[0] assert factory compiled_classname = compiled.create(evaluator, typ.name.value) - if isinstance(index, Class): - index_obj = index - else: - index_obj = compiled.create(evaluator, index) - result = \ - evaluator.execute_evaluated(factory, compiled_classname, index_obj) + + result = set() + for indextyp in dereferencedindextypes: + result |= \ + evaluator.execute_evaluated(factory, compiled_classname, indextyp) if result: return result else: From 85023a22aa1e5ca0c3e661eec607adf725b0a5bc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Dec 2015 19:07:13 +0100 Subject: [PATCH 04/26] Not implemented classes should not default to everything --- jedi/evaluate/pep0484.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 771a7a14..3730e7bc 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -21,7 +21,8 @@ x support for type hint comments `# type: (int, str) -> int`. See comment from from itertools import chain import os -from jedi.parser import Parser, load_grammar, ParseError, ParserWithRecovery +from jedi.parser import \ + Parser, load_grammar, ParseError, ParserWithRecovery, tree from jedi.evaluate.cache import memoize_default from jedi.evaluate import compiled from jedi import debug @@ -107,13 +108,16 @@ def get_types_for_typing_module(evaluator, typ, trailer): assert len(factories) == 1 factory = list(factories)[0] assert factory + function_body_nodes = factory.children[4].children + valid_classnames = {child.name.value + for child in function_body_nodes + if isinstance(child, tree.Class)} + if typ.name.value not in valid_classnames: + return None compiled_classname = compiled.create(evaluator, typ.name.value) result = set() for indextyp in dereferencedindextypes: result |= \ evaluator.execute_evaluated(factory, compiled_classname, indextyp) - if result: - return result - else: - return None + return result From e688a498abee778128837baae43a0fcd1bb8e482 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Dec 2015 19:32:31 +0100 Subject: [PATCH 05/26] Add sets and iterable/iterator --- jedi/evaluate/jedi_typing.py | 31 ++++++++++++++++++++++++++++++- test/completion/pep0484.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py index 14c74570..8d66258f 100644 --- a/jedi/evaluate/jedi_typing.py +++ b/jedi/evaluate/jedi_typing.py @@ -7,7 +7,19 @@ from collections import abc def factory(typing_name, indextype): - class Sequence(abc.Sequence): + class Iterable(abc.Iterable): + def __iter__(self): + yield indextype() + + class Iterator(Iterable, abc.Iterator): + def next(self): + """ needed for python 2 """ + return self.__next__() + + def __next__(self): + return indextype() + + class Sequence(Iterable, abc.Sequence): def __getitem__(self, index: int): return indextype() @@ -15,12 +27,29 @@ def factory(typing_name, indextype): def __setitem__(self, index: int, value: indextype): pass + def __delitem__(self, index: int, value: indextype): + pass + class List(MutableSequence, list): + pass + + class AbstractSet(Iterable, abc.Set): + pass + + class MutableSet(AbstractSet, abc.MutableSet): + def add(item: indextype): + pass + + def discard(item: indextype): pass dct = { "Sequence": Sequence, "MutableSequence": MutableSequence, "List": List, + "Iterable": Iterable, + "Iterator": Iterator, + "AbstractSet": AbstractSet, + "MutableSet": MutableSet, } return dct[typing_name] diff --git a/test/completion/pep0484.py b/test/completion/pep0484.py index 75742846..f5c2f93d 100644 --- a/test/completion/pep0484.py +++ b/test/completion/pep0484.py @@ -192,3 +192,35 @@ def we_can_has_sequence( u.a #? float() u[1] + +def iterators( + ps: typing.Iterable[int], + qs: typing.Iterator[str], + rs: typing.Sequence["B"], + ts: typing.AbstractSet["float"]): + for p in ps: + #? int() + p + #? + next(ps) + for q in qs: + #? str() + q + #? str() + next(qs) + for r in rs: + #? B() + r + #? + next(rs) + for t in ts: + #? float() + t + +def sets( + p: typing.AbstractSet[int], + q: typing.MutableSet[float]): + #? [] + p.a + #? ["add"] + q.a From 90c4ca8c043897aa4c19f639fb81b00e0177d03b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Dec 2015 19:34:48 +0100 Subject: [PATCH 06/26] should obviously keep typing.py parsable in python 2 --- jedi/evaluate/jedi_typing.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py index 8d66258f..a9fc813c 100644 --- a/jedi/evaluate/jedi_typing.py +++ b/jedi/evaluate/jedi_typing.py @@ -20,15 +20,11 @@ def factory(typing_name, indextype): return indextype() class Sequence(Iterable, abc.Sequence): - def __getitem__(self, index: int): + def __getitem__(self, index): return indextype() class MutableSequence(Sequence, abc.MutableSequence): - def __setitem__(self, index: int, value: indextype): - pass - - def __delitem__(self, index: int, value: indextype): - pass + pass class List(MutableSequence, list): pass @@ -37,11 +33,7 @@ def factory(typing_name, indextype): pass class MutableSet(AbstractSet, abc.MutableSet): - def add(item: indextype): - pass - - def discard(item: indextype): - pass + pass dct = { "Sequence": Sequence, From 67cbc5ebd12a8beb32c80c5dc14dbbaddca408a0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Dec 2015 20:00:08 +0100 Subject: [PATCH 07/26] made code slightly more pytho2 friendly --- jedi/evaluate/jedi_typing.py | 6 ++- jedi/evaluate/pep0484.py | 10 ++--- test/completion/pep0484.py | 67 ---------------------------- test/completion/pep0484_typing.py | 73 +++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 73 deletions(-) create mode 100644 test/completion/pep0484_typing.py diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py index a9fc813c..2c547482 100644 --- a/jedi/evaluate/jedi_typing.py +++ b/jedi/evaluate/jedi_typing.py @@ -3,7 +3,11 @@ This module is not intended to be used in jedi, rather it will be fed to the jedi-parser to replace classes in the typing module """ -from collections import abc +try: + from collections import abc +except ImportError: + # python 2 + import collections as abc def factory(typing_name, indextype): diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 3730e7bc..4b507a56 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -56,7 +56,7 @@ def _fix_forward_reference(evaluator, item, module): element.parent = module return evaluator.eval_element(element) else: - return {item} + return set([item]) @memoize_default(None, evaluator_is_first_arg=True) @@ -95,7 +95,7 @@ def get_types_for_typing_module(evaluator, typ, trailer): # actually the PEP-0484 typing module and not some other indextypes = evaluator.eval_element(trailer.children[1]) if not isinstance(indextypes, set): - indextypes = {indextypes} + indextypes = set([indextypes]) module = trailer.get_parent_until() dereferencedindextypes = set() @@ -109,9 +109,9 @@ def get_types_for_typing_module(evaluator, typ, trailer): factory = list(factories)[0] assert factory function_body_nodes = factory.children[4].children - valid_classnames = {child.name.value - for child in function_body_nodes - if isinstance(child, tree.Class)} + valid_classnames = set(child.name.value + for child in function_body_nodes + if isinstance(child, tree.Class)) if typ.name.value not in valid_classnames: return None compiled_classname = compiled.create(evaluator, typ.name.value) diff --git a/test/completion/pep0484.py b/test/completion/pep0484.py index f5c2f93d..fc08460f 100644 --- a/test/completion/pep0484.py +++ b/test/completion/pep0484.py @@ -157,70 +157,3 @@ Y = int def just_because_we_can(x: "flo" + "at"): #? float() x - -import typing -def we_can_has_sequence( - p: typing.Sequence[int], - q: typing.Sequence[B], - r: "typing.Sequence[int]", - s: typing.Sequence["int"], - t: typing.MutableSequence[dict], - u: typing.List[float]): - #? ["count"] - p.c - #? int() - p[1] - #? ["count"] - q.c - #? B() - q[1] - #? ["count"] - r.c - #? int() - r[1] - #? ["count"] - s.c - #? int() - s[1] - #? [] - s.a - #? ["append"] - t.a - #? dict() - t[1] - #? ["append"] - u.a - #? float() - u[1] - -def iterators( - ps: typing.Iterable[int], - qs: typing.Iterator[str], - rs: typing.Sequence["B"], - ts: typing.AbstractSet["float"]): - for p in ps: - #? int() - p - #? - next(ps) - for q in qs: - #? str() - q - #? str() - next(qs) - for r in rs: - #? B() - r - #? - next(rs) - for t in ts: - #? float() - t - -def sets( - p: typing.AbstractSet[int], - q: typing.MutableSet[float]): - #? [] - p.a - #? ["add"] - q.a diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py new file mode 100644 index 00000000..ef5ff908 --- /dev/null +++ b/test/completion/pep0484_typing.py @@ -0,0 +1,73 @@ +# python >= 3.2 +import typing +class B: + pass + +def we_can_has_sequence( + p: typing.Sequence[int], + q: typing.Sequence[B], + r: "typing.Sequence[int]", + s: typing.Sequence["int"], + t: typing.MutableSequence[dict], + u: typing.List[float]): + #? ["count"] + p.c + #? int() + p[1] + #? ["count"] + q.c + #? B() + q[1] + #? ["count"] + r.c + #? int() + r[1] + #? ["count"] + s.c + #? int() + s[1] + #? [] + s.a + #? ["append"] + t.a + #? dict() + t[1] + #? ["append"] + u.a + #? float() + u[1] + +def iterators( + ps: typing.Iterable[int], + qs: typing.Iterator[str], + rs: typing.Sequence["ForwardReference"], + ts: typing.AbstractSet["float"]): + for p in ps: + #? int() + p + #? + next(ps) + for q in qs: + #? str() + q + #? str() + next(qs) + for r in rs: + #? ForwardReference() + r + #? + next(rs) + for t in ts: + #? float() + t + +def sets( + p: typing.AbstractSet[int], + q: typing.MutableSet[float]): + #? [] + p.a + #? ["add"] + q.a + +class ForwardReference: + pass From 5948c63cf95f01cf4360d58d94b6c70b9cce75b1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Dec 2015 20:13:33 +0100 Subject: [PATCH 08/26] Make the classes descriptions look better --- not sure whether this is a good idea --- jedi/evaluate/pep0484.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 4b507a56..5d0ec018 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -120,4 +120,6 @@ def get_types_for_typing_module(evaluator, typ, trailer): for indextyp in dereferencedindextypes: result |= \ evaluator.execute_evaluated(factory, compiled_classname, indextyp) + for singleresult in result: + singleresult.name.value += "[%s]" % indextyp.name return result From 10f5e15325dd065d2d1ffee35e96b3d36817b48c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Dec 2015 23:05:01 +0100 Subject: [PATCH 09/26] I feel this is a nicer solution. Forward Reference busting should be part of the annotation resolving. It doesn not have anything to do with the typing module (and should indeed also happen if someone writes his own types outside of the typing module) --- jedi/evaluate/pep0484.py | 50 +++++++++++++++++++++----------------- test/completion/pep0484.py | 4 --- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 5d0ec018..db80dd79 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -30,33 +30,45 @@ from jedi import debug def _evaluate_for_annotation(evaluator, annotation): if annotation is not None: - definitions = set() - module = annotation.get_parent_until() - for definition in evaluator.eval_element(annotation): - definitions |= \ - _fix_forward_reference(evaluator, definition, module) + dereferenced_annotation = _fix_forward_reference(evaluator, annotation) + definitions = evaluator.eval_element(dereferenced_annotation) return list(chain.from_iterable( evaluator.execute(d) for d in definitions)) else: return [] -def _fix_forward_reference(evaluator, item, module): - if (isinstance(item, compiled.CompiledObject) and - isinstance(item.obj, str)): +def _fix_forward_reference(evaluator, item): + """ + Gets something from the parse tree, and replaces any string literal + in there with the result of evaluating that string at the bottom of the + module + """ + if isinstance(item, tree.String): + compiledobjects = evaluator.eval_element(item) + assert len(compiledobjects) == 1 + compiledobject = list(compiledobjects)[0] try: - p = Parser( - load_grammar(), item.obj, start='eval_input') + p = Parser(load_grammar(), compiledobject.obj, start='eval_input') element = p.get_parsed_node() except ParseError: - debug.warning('Annotation not parsed: %s' % item.obj) - return set() + debug.warning('Annotation not parsed: %s' % compiledobject.obj) + return item else: + module = item.get_parent_until() p.position_modifier.line = module.end_pos[0] element.parent = module - return evaluator.eval_element(element) - else: - return set([item]) + dereferenced = _fix_forward_reference(evaluator, element) + return dereferenced + if isinstance(item, tree.Node): + newnode = tree.Node(item.type, []) + for child in item.children: + newchild = _fix_forward_reference(evaluator, child) + newchild.parent = newnode + newnode.children.append(newchild) + newnode.parent = item.parent + return newnode + return item @memoize_default(None, evaluator_is_first_arg=True) @@ -97,12 +109,6 @@ def get_types_for_typing_module(evaluator, typ, trailer): if not isinstance(indextypes, set): indextypes = set([indextypes]) - module = trailer.get_parent_until() - dereferencedindextypes = set() - for indextyp in indextypes: - dereferencedindextypes |= \ - _fix_forward_reference(evaluator, indextyp, module) - typing = _get_typing_replacement_module() factories = evaluator.find_types(typing, "factory") assert len(factories) == 1 @@ -117,7 +123,7 @@ def get_types_for_typing_module(evaluator, typ, trailer): compiled_classname = compiled.create(evaluator, typ.name.value) result = set() - for indextyp in dereferencedindextypes: + for indextyp in indextypes: result |= \ evaluator.execute_evaluated(factory, compiled_classname, indextyp) for singleresult in result: diff --git a/test/completion/pep0484.py b/test/completion/pep0484.py index fc08460f..298a7a4f 100644 --- a/test/completion/pep0484.py +++ b/test/completion/pep0484.py @@ -153,7 +153,3 @@ def function_with_assined_class_in_reference(x: X, y: "Y"): #? int() y Y = int - -def just_because_we_can(x: "flo" + "at"): - #? float() - x From 7b9731250975a2b5e2dbf2b11473309091028d0f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 31 Dec 2015 01:29:03 +0100 Subject: [PATCH 10/26] tuples and mappings in typing --- jedi/evaluate/iterable.py | 11 ++-- jedi/evaluate/jedi_typing.py | 49 ++++++++++++++++-- jedi/evaluate/pep0484.py | 43 ++++++++++++---- test/completion/pep0484_typing.py | 85 +++++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 21 deletions(-) diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index 957f9784..f92d1a97 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -564,13 +564,16 @@ def py__getitem__(evaluator, types, trailer): from jedi.evaluate.representation import Class result = set() + trailer_op, node, trailer_cl = trailer.children + assert trailer_op == "[" + assert trailer_cl == "]" # special case: PEP0484 typing module, see # https://github.com/davidhalter/jedi/issues/663 for typ in list(types): if isinstance(typ, Class): typing_module_types = \ - pep0484.get_types_for_typing_module(evaluator, typ, trailer) + pep0484.get_types_for_typing_module(evaluator, typ, node) if typing_module_types is not None: types.remove(typ) result |= typing_module_types @@ -579,12 +582,6 @@ def py__getitem__(evaluator, types, trailer): # all consumed by special cases return result - trailer_op, node, trailer_cl = trailer.children[:3] - assert trailer_op == "[" - if trailer_cl != "]": - debug.warning("No support for complex indices: %s" % trailer) - return result - for index in create_index_types(evaluator, node): if isinstance(index, (compiled.CompiledObject, Slice)): index = index.obj diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py index 2c547482..62f52fd4 100644 --- a/jedi/evaluate/jedi_typing.py +++ b/jedi/evaluate/jedi_typing.py @@ -10,10 +10,10 @@ except ImportError: import collections as abc -def factory(typing_name, indextype): +def factory(typing_name, indextypes): class Iterable(abc.Iterable): def __iter__(self): - yield indextype() + yield indextypes[0]() class Iterator(Iterable, abc.Iterator): def next(self): @@ -21,11 +21,11 @@ def factory(typing_name, indextype): return self.__next__() def __next__(self): - return indextype() + return indextypes[0]() class Sequence(Iterable, abc.Sequence): def __getitem__(self, index): - return indextype() + return indextypes[0]() class MutableSequence(Sequence, abc.MutableSequence): pass @@ -33,12 +33,46 @@ def factory(typing_name, indextype): class List(MutableSequence, list): pass + class Tuple(Sequence, tuple): + def __getitem__(self, index): + return indextypes[index]() + class AbstractSet(Iterable, abc.Set): pass class MutableSet(AbstractSet, abc.MutableSet): pass + class KeysView(Iterable, abc.KeysView): + pass + + class ValuesView(abc.ValuesView): + def __iter__(self): + yield indextypes[1]() + + class ItemsView(abc.ItemsView): + def __iter__(self): + yield Tuple() + + class Mapping(Iterable, abc.Mapping): + def __getitem__(self, item): + return indextypes[1]() + + def keys(self): + return KeysView() + + def values(self): + return ValuesView() + + def items(self): + return ItemsView() + + class MutableMapping(Mapping, abc.MutableMapping): + pass + + class Dict(MutableMapping, dict): + pass + dct = { "Sequence": Sequence, "MutableSequence": MutableSequence, @@ -47,5 +81,12 @@ def factory(typing_name, indextype): "Iterator": Iterator, "AbstractSet": AbstractSet, "MutableSet": MutableSet, + "Mapping": Mapping, + "MutableMapping": MutableMapping, + "Tuple": Tuple, + "KeysView": KeysView, + "ItemsView": ItemsView, + "ValuesView": ValuesView, + "Dict": Dict, } return dct[typing_name] diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index db80dd79..422ca85f 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -18,7 +18,7 @@ x support for type hint comments `# type: (int, str) -> int`. See comment from Guido https://github.com/davidhalter/jedi/issues/662 """ -from itertools import chain +import itertools import os from jedi.parser import \ @@ -32,7 +32,7 @@ def _evaluate_for_annotation(evaluator, annotation): if annotation is not None: dereferenced_annotation = _fix_forward_reference(evaluator, annotation) definitions = evaluator.eval_element(dereferenced_annotation) - return list(chain.from_iterable( + return list(itertools.chain.from_iterable( evaluator.execute(d) for d in definitions)) else: return [] @@ -97,7 +97,8 @@ def _get_typing_replacement_module(): return p.module -def get_types_for_typing_module(evaluator, typ, trailer): +def get_types_for_typing_module(evaluator, typ, node): + from jedi.evaluate.iterable import FakeSequence if not typ.base.get_parent_until().name.value == "typing": return None # we assume that any class using [] in a module called @@ -105,9 +106,10 @@ def get_types_for_typing_module(evaluator, typ, trailer): # should be replaced by that class. This is not 100% # airtight but I don't have a better idea to check that it's # actually the PEP-0484 typing module and not some other - indextypes = evaluator.eval_element(trailer.children[1]) - if not isinstance(indextypes, set): - indextypes = set([indextypes]) + if tree.is_node(node, "subscriptlist"): + nodes = node.children[::2] # skip the commas + else: + nodes = [node] typing = _get_typing_replacement_module() factories = evaluator.find_types(typing, "factory") @@ -123,9 +125,30 @@ def get_types_for_typing_module(evaluator, typ, trailer): compiled_classname = compiled.create(evaluator, typ.name.value) result = set() - for indextyp in indextypes: - result |= \ - evaluator.execute_evaluated(factory, compiled_classname, indextyp) + # don't know what the last parameter is for, this seems to work :) + args = FakeSequence(evaluator, nodes, "x-type") + + result |= evaluator.execute_evaluated(factory, compiled_classname, args) + human_nodes = [] + for node in nodes: + evalled_node = evaluator.eval_element(node) + if len(evalled_node) != 1: + human_nodes.append("???") + continue + evalled_node = list(evalled_node)[0] + try: + human_nodes.append(str(evalled_node.name)) + except AttributeError: + pass + else: + continue + try: + human_nodes.append(evalled_node.obj.__name__) + except AttributeError: + pass + else: + continue + human_nodes.append("???") for singleresult in result: - singleresult.name.value += "[%s]" % indextyp.name + singleresult.name.value += "[%s]" % ", ".join(human_nodes) return result diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index ef5ff908..8b8ad852 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -69,5 +69,90 @@ def sets( #? ["add"] q.a +def tuple( + p: typing.Tuple[int], + q: typing.Tuple[int, str, float], + r: typing.Tuple[B, ...]): + #? int() + p[0] + #? int() + q[0] + #? str() + q[1] + #? float() + q[2] + #? B() + r[0] + ##? B() --- TODO fix support for arbitrary length + r[1] + #? B() + r[2] + #? B() + r[10000] + i, s, f = q + #? int() + i + ##? str() --- TODO fix support for tuple assignment + s + ##? float() --- TODO fix support for tuple assignment + f + +class Key: + pass + +class Value: + pass + +def mapping( + p: typing.Mapping[Key, Value], + q: typing.MutableMapping[Key, Value], + d: typing.Dict[Key, Value], + r: typing.KeysView[Key], + s: typing.ValuesView[Value], + t: typing.ItemsView[Key, Value]): + #? [] + p.setd + #? ["setdefault"] + q.setd + #? ["setdefault"] + d.setd + #? Value() + p[1] + for key in p: + #? Key() + key + for key in p.keys(): + #? Key() + key + for value in p.values(): + #? Value() + value + for item in p.items(): + #? Key() + item[0] + #? Value() + item[1] + (key, value) = item + #? Key() + key + ##? Value() --- TODO fix support for tuple assignment + value + for key, value in p.items(): + #? Key() + key + ##? Value() --- TODO fix support for tuple assignment + value + for key in r: + #? Key() + key + for value in s: + #? Value() + value + for key, value in t: + #? Key() + key + ##? Value() --- TODO fix support for tuple assignment + value + class ForwardReference: pass From 3852431549d3e7c4f5c99d0f907c71b40110f8df Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 31 Dec 2015 01:59:34 +0100 Subject: [PATCH 11/26] typing.Union and typing.Optional --- jedi/evaluate/pep0484.py | 7 +++++++ test/completion/pep0484_typing.py | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 422ca85f..8b4381ff 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -110,6 +110,13 @@ def get_types_for_typing_module(evaluator, typ, node): nodes = node.children[::2] # skip the commas else: nodes = [node] + del node + + # hacked in Union and Optional, since it's hard to do nicely in parsed code + if typ.name.value == "Union": + return set().union(*[evaluator.eval_element(node) for node in nodes]) + if typ.name.value == "Optional": + return evaluator.eval_element(nodes[0]) typing = _get_typing_replacement_module() factories = evaluator.find_types(typing, "factory") diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index 8b8ad852..6b085fc2 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -154,5 +154,32 @@ def mapping( ##? Value() --- TODO fix support for tuple assignment value +def union( + p: typing.Union[int], + q: typing.Union[int, int], + r: typing.Union[int, str, "int"], + s: typing.Union[int, typing.Union[str, "typing.Union['float', 'dict']"]], + t: typing.Union[int, None]): + #? int() + p + #? int() + q + #? int() str() + r + #? int() str() float() dict() + s + #? int() + t + +def optional( + p: typing.Optional[int]): + """ + Optional does not do anything special. However it should be recognised + as being of that type. Jedi doesn't do anything with the extra into that + it can be None as well + """ + #? int() + p + class ForwardReference: pass From 409ee5568a1937c055dc1b944d4b0311abb7ff12 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 31 Dec 2015 11:04:26 +0100 Subject: [PATCH 12/26] test with different ways of importing the typing module --- test/completion/pep0484_typing.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index 6b085fc2..0467c8c5 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -183,3 +183,18 @@ def optional( class ForwardReference: pass + +import typing as t +def union2(x: t.Union[int, str]): + #? int() str() + x + +from typing import Union +def union3(x: Union[int, str]): + #? int() str() + x + +from typing import Union as U +def union4(x: U[int, str]): + #? int() str() + x From 1b787e2a110a099e9b100ab89b3911aa682e8f23 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 31 Dec 2015 11:19:59 +0100 Subject: [PATCH 13/26] add test to check instanciated subclasses --- test/completion/pep0484_typing.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index 0467c8c5..2dfc50fc 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -198,3 +198,27 @@ from typing import Union as U def union4(x: U[int, str]): #? int() str() x + +class TestDict(typing.Dict[str, int]): + def setdud(self): + pass + +def testdict(x: TestDict): + #? ["setdud", "setdefault"] + x.setd + for key in x.keys(): + #? str() + key + for value in x.values(): + #? int() + value + +x = TestDict() +#? ["setdud", "setdefault"] +x.setd +for key in x.keys(): + #? str() + key +for value in x.values(): + #? int() + value From 9d7e1ce81b9e24fb1a05c3dbc4fa43ed70fdc69c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 Jan 2016 13:40:14 +0100 Subject: [PATCH 14/26] add the typing module for testing --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 526092fc..1229d735 100644 --- a/tox.ini +++ b/tox.ini @@ -8,6 +8,8 @@ deps = docopt # coloroma for colored debug output colorama +# for testing the typing module + typing setenv = # https://github.com/tomchristie/django-rest-framework/issues/1957 # tox corrupts __pycache__, solution from here: From 59161c0b5d3224be3e622367fc95fb0d31b211b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 Jan 2016 10:51:06 +0100 Subject: [PATCH 15/26] fix FakeSequence type --- jedi/evaluate/iterable.py | 3 +++ jedi/evaluate/pep0484.py | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index f92d1a97..0c6c20ce 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -431,6 +431,9 @@ class ImplicitTuple(_FakeArray): class FakeSequence(_FakeArray): def __init__(self, evaluator, sequence_values, type): + """ + type should be one of "tuple", "list" + """ super(FakeSequence, self).__init__(evaluator, sequence_values, type) self._sequence_values = sequence_values diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 8b4381ff..56efcd67 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -132,8 +132,7 @@ def get_types_for_typing_module(evaluator, typ, node): compiled_classname = compiled.create(evaluator, typ.name.value) result = set() - # don't know what the last parameter is for, this seems to work :) - args = FakeSequence(evaluator, nodes, "x-type") + args = FakeSequence(evaluator, nodes, "tuple") result |= evaluator.execute_evaluated(factory, compiled_classname, args) human_nodes = [] From a5fc149f9d4d9c832e242004d507ad9b225dd94b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 Jan 2016 10:57:38 +0100 Subject: [PATCH 16/26] use jedi.common.unite in flatten array of sets --- jedi/evaluate/pep0484.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 56efcd67..c5499f16 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -24,6 +24,7 @@ import os from jedi.parser import \ Parser, load_grammar, ParseError, ParserWithRecovery, tree from jedi.evaluate.cache import memoize_default +from jedi.common import unite from jedi.evaluate import compiled from jedi import debug @@ -114,7 +115,7 @@ def get_types_for_typing_module(evaluator, typ, node): # hacked in Union and Optional, since it's hard to do nicely in parsed code if typ.name.value == "Union": - return set().union(*[evaluator.eval_element(node) for node in nodes]) + return unite(evaluator.eval_element(node) for node in nodes) if typ.name.value == "Optional": return evaluator.eval_element(nodes[0]) From ae701b2f9a23f9123327dd9bd87272269f5a8f72 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 Jan 2016 12:43:23 +0100 Subject: [PATCH 17/26] Support for typing.Tuple[type, ...] --- jedi/evaluate/jedi_typing.py | 7 ++++++- test/completion/pep0484_typing.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py index 62f52fd4..e34ee989 100644 --- a/jedi/evaluate/jedi_typing.py +++ b/jedi/evaluate/jedi_typing.py @@ -35,7 +35,12 @@ def factory(typing_name, indextypes): class Tuple(Sequence, tuple): def __getitem__(self, index): - return indextypes[index]() + if indextypes[1] == ...: + # https://www.python.org/dev/peps/pep-0484/#the-typing-module + # Tuple[int, ...] means a tuple of ints of indetermined length + return indextypes[0]() + else: + return indextypes[index]() class AbstractSet(Iterable, abc.Set): pass diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index 2dfc50fc..74a99e51 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -83,7 +83,7 @@ def tuple( q[2] #? B() r[0] - ##? B() --- TODO fix support for arbitrary length + #? B() r[1] #? B() r[2] From b4999063987a4c03ef04eb05fda5c615c9bb3f5a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 Jan 2016 16:12:43 +0100 Subject: [PATCH 18/26] Reverted 10f5e1 --- needed some more work to get it working again --- jedi/evaluate/pep0484.py | 64 +++++++++++++++++--------------------- test/completion/pep0484.py | 4 +++ 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index c5499f16..1c7ca2fe 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -31,45 +31,36 @@ from jedi import debug def _evaluate_for_annotation(evaluator, annotation): if annotation is not None: - dereferenced_annotation = _fix_forward_reference(evaluator, annotation) - definitions = evaluator.eval_element(dereferenced_annotation) + definitions = evaluator.eval_element( + _fix_forward_reference(evaluator, annotation)) return list(itertools.chain.from_iterable( evaluator.execute(d) for d in definitions)) else: return [] -def _fix_forward_reference(evaluator, item): - """ - Gets something from the parse tree, and replaces any string literal - in there with the result of evaluating that string at the bottom of the - module - """ - if isinstance(item, tree.String): - compiledobjects = evaluator.eval_element(item) - assert len(compiledobjects) == 1 - compiledobject = list(compiledobjects)[0] +def _fix_forward_reference(evaluator, node): + evaled_nodes = evaluator.eval_element(node) + if len(evaled_nodes) != 1: + debug.warning("Eval'ed typing index %s should lead to 1 object, " + " not %s" % (node, evaled_nodes)) + return node + evaled_node = list(evaled_nodes)[0] + if isinstance(evaled_node, compiled.CompiledObject) and \ + isinstance(evaled_node.obj, str): try: - p = Parser(load_grammar(), compiledobject.obj, start='eval_input') - element = p.get_parsed_node() + p = Parser(load_grammar(), evaled_node.obj, start='eval_input') + newnode = p.get_parsed_node() except ParseError: - debug.warning('Annotation not parsed: %s' % compiledobject.obj) - return item + debug.warning('Annotation not parsed: %s' % evaled_node.obj) + return node else: - module = item.get_parent_until() + module = node.get_parent_until() p.position_modifier.line = module.end_pos[0] - element.parent = module - dereferenced = _fix_forward_reference(evaluator, element) - return dereferenced - if isinstance(item, tree.Node): - newnode = tree.Node(item.type, []) - for child in item.children: - newchild = _fix_forward_reference(evaluator, child) - newchild.parent = newnode - newnode.children.append(newchild) - newnode.parent = item.parent - return newnode - return item + newnode.parent = module + return newnode + else: + return node @memoize_default(None, evaluator_is_first_arg=True) @@ -113,6 +104,8 @@ def get_types_for_typing_module(evaluator, typ, node): nodes = [node] del node + nodes = [_fix_forward_reference(evaluator, node) for node in nodes] + # hacked in Union and Optional, since it's hard to do nicely in parsed code if typ.name.value == "Union": return unite(evaluator.eval_element(node) for node in nodes) @@ -132,25 +125,24 @@ def get_types_for_typing_module(evaluator, typ, node): return None compiled_classname = compiled.create(evaluator, typ.name.value) - result = set() args = FakeSequence(evaluator, nodes, "tuple") - result |= evaluator.execute_evaluated(factory, compiled_classname, args) + result = evaluator.execute_evaluated(factory, compiled_classname, args) human_nodes = [] for node in nodes: - evalled_node = evaluator.eval_element(node) - if len(evalled_node) != 1: + evaled_node = evaluator.eval_element(node) + if len(evaled_node) != 1: human_nodes.append("???") continue - evalled_node = list(evalled_node)[0] + evaled_node = list(evaled_node)[0] try: - human_nodes.append(str(evalled_node.name)) + human_nodes.append(str(evaled_node.name)) except AttributeError: pass else: continue try: - human_nodes.append(evalled_node.obj.__name__) + human_nodes.append(evaled_node.obj.__name__) except AttributeError: pass else: diff --git a/test/completion/pep0484.py b/test/completion/pep0484.py index 298a7a4f..fc08460f 100644 --- a/test/completion/pep0484.py +++ b/test/completion/pep0484.py @@ -153,3 +153,7 @@ def function_with_assined_class_in_reference(x: X, y: "Y"): #? int() y Y = int + +def just_because_we_can(x: "flo" + "at"): + #? float() + x From 885f7cb06873ca30ed483e80816ec724399de49e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 Jan 2016 16:45:12 +0100 Subject: [PATCH 19/26] fix for iterators -- should start working when py__iter__ gets fixed: https://github.com/davidhalter/jedi/pull/663\#issuecomment-172317854 --- jedi/evaluate/jedi_typing.py | 22 ++++++++++++++++++---- test/completion/pep0484_typing.py | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py index e34ee989..2fd0e0d8 100644 --- a/jedi/evaluate/jedi_typing.py +++ b/jedi/evaluate/jedi_typing.py @@ -13,7 +13,8 @@ except ImportError: def factory(typing_name, indextypes): class Iterable(abc.Iterable): def __iter__(self): - yield indextypes[0]() + while True: + yield indextypes[0]() class Iterator(Iterable, abc.Iterator): def next(self): @@ -23,10 +24,14 @@ def factory(typing_name, indextypes): def __next__(self): return indextypes[0]() - class Sequence(Iterable, abc.Sequence): + class Sequence(abc.Sequence): def __getitem__(self, index): return indextypes[0]() + def __len__(self): + import sys + return sys.maxint + class MutableSequence(Sequence, abc.MutableSequence): pass @@ -42,6 +47,13 @@ def factory(typing_name, indextypes): else: return indextypes[index]() + def __len__(self): + if indextypes[1] == ...: + import sys + return sys.maxint + else: + return len(indextypes) + class AbstractSet(Iterable, abc.Set): pass @@ -53,11 +65,13 @@ def factory(typing_name, indextypes): class ValuesView(abc.ValuesView): def __iter__(self): - yield indextypes[1]() + while True: + yield indextypes[1]() class ItemsView(abc.ItemsView): def __iter__(self): - yield Tuple() + while True: + yield Tuple() class Mapping(Iterable, abc.Mapping): def __getitem__(self, item): diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index 74a99e51..b3fe6055 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -47,6 +47,16 @@ def iterators( p #? next(ps) + a, b = ps + #? int() + a + ##? int() --- TODO fix support for tuple assignment + # https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854 + # test below is just to make sure that in case it gets fixed by accident + # these tests will be fixed as well the way they should be + #? + b + for q in qs: #? str() q @@ -93,8 +103,12 @@ def tuple( #? int() i ##? str() --- TODO fix support for tuple assignment + # https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854 + #? s ##? float() --- TODO fix support for tuple assignment + # https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854 + #? f class Key: @@ -136,11 +150,15 @@ def mapping( #? Key() key ##? Value() --- TODO fix support for tuple assignment + # https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854 + #? value for key, value in p.items(): #? Key() key ##? Value() --- TODO fix support for tuple assignment + # https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854 + #? value for key in r: #? Key() @@ -152,6 +170,8 @@ def mapping( #? Key() key ##? Value() --- TODO fix support for tuple assignment + # https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854 + #? value def union( From b316fb94c45911381f00b36721c48b5e3ed26efb Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 Jan 2016 17:05:29 +0100 Subject: [PATCH 20/26] enable tests for the value type in tuple assignment from typing.Mapping[].items() --- test/completion/pep0484_typing.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index b3fe6055..f31542ea 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -149,16 +149,12 @@ def mapping( (key, value) = item #? Key() key - ##? Value() --- TODO fix support for tuple assignment - # https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854 - #? + #? Value() value for key, value in p.items(): #? Key() key - ##? Value() --- TODO fix support for tuple assignment - # https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854 - #? + #? Value() value for key in r: #? Key() @@ -169,9 +165,7 @@ def mapping( for key, value in t: #? Key() key - ##? Value() --- TODO fix support for tuple assignment - # https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854 - #? + #? Value() value def union( From 941da773f61f398da2e2ba5ca8a1d8999480915a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 Jan 2016 17:05:31 +0100 Subject: [PATCH 21/26] temporary fix for typing.Mapping[...].items(), can be removed after #683 is fixed --- jedi/evaluate/jedi_typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py index 2fd0e0d8..e7d77ff9 100644 --- a/jedi/evaluate/jedi_typing.py +++ b/jedi/evaluate/jedi_typing.py @@ -71,7 +71,7 @@ def factory(typing_name, indextypes): class ItemsView(abc.ItemsView): def __iter__(self): while True: - yield Tuple() + yield (indextypes[0](), indextypes[1]()) class Mapping(Iterable, abc.Mapping): def __getitem__(self, item): From 442d948e322ea0099777f091752bb60002d02a30 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 Jan 2016 18:04:59 +0100 Subject: [PATCH 22/26] I don't need the __len__ for __iter__ to work (eventually), so leaving it out for now --- jedi/evaluate/jedi_typing.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py index e7d77ff9..52b0ab89 100644 --- a/jedi/evaluate/jedi_typing.py +++ b/jedi/evaluate/jedi_typing.py @@ -28,10 +28,6 @@ def factory(typing_name, indextypes): def __getitem__(self, index): return indextypes[0]() - def __len__(self): - import sys - return sys.maxint - class MutableSequence(Sequence, abc.MutableSequence): pass @@ -47,13 +43,6 @@ def factory(typing_name, indextypes): else: return indextypes[index]() - def __len__(self): - if indextypes[1] == ...: - import sys - return sys.maxint - else: - return len(indextypes) - class AbstractSet(Iterable, abc.Set): pass From c9bf521efd0b8e63a4e07f993d30d35a04a4733b Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 Jan 2016 22:10:52 +0100 Subject: [PATCH 23/26] remove renaming of class based on parameters --- jedi/evaluate/pep0484.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 1c7ca2fe..ac08908c 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -128,26 +128,4 @@ def get_types_for_typing_module(evaluator, typ, node): args = FakeSequence(evaluator, nodes, "tuple") result = evaluator.execute_evaluated(factory, compiled_classname, args) - human_nodes = [] - for node in nodes: - evaled_node = evaluator.eval_element(node) - if len(evaled_node) != 1: - human_nodes.append("???") - continue - evaled_node = list(evaled_node)[0] - try: - human_nodes.append(str(evaled_node.name)) - except AttributeError: - pass - else: - continue - try: - human_nodes.append(evaled_node.obj.__name__) - except AttributeError: - pass - else: - continue - human_nodes.append("???") - for singleresult in result: - singleresult.name.value += "[%s]" % ", ".join(human_nodes) return result From e267f6365786582dd325ec9de98854c459921b52 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 Jan 2016 22:53:48 +0100 Subject: [PATCH 24/26] python 2.7 compatibility, typing module tested with docstring, so that it can also be tested in python 2.7 --- jedi/evaluate/pep0484.py | 6 +- test/completion/pep0484_typing.py | 125 ++++++++++++++++++------------ 2 files changed, 78 insertions(+), 53 deletions(-) diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index ac08908c..6c7f6e8e 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -27,6 +27,7 @@ from jedi.evaluate.cache import memoize_default from jedi.common import unite from jedi.evaluate import compiled from jedi import debug +from jedi import _compatibility def _evaluate_for_annotation(evaluator, annotation): @@ -49,7 +50,8 @@ def _fix_forward_reference(evaluator, node): if isinstance(evaled_node, compiled.CompiledObject) and \ isinstance(evaled_node.obj, str): try: - p = Parser(load_grammar(), evaled_node.obj, start='eval_input') + p = Parser(load_grammar(), _compatibility.unicode(evaled_node.obj), + start='eval_input') newnode = p.get_parsed_node() except ParseError: debug.warning('Annotation not parsed: %s' % evaled_node.obj) @@ -84,7 +86,7 @@ def _get_typing_replacement_module(): typing_path = os.path.abspath(os.path.join(__file__, "../jedi_typing.py")) with open(typing_path) as f: - code = f.read() + code = _compatibility.unicode(f.read()) p = ParserWithRecovery(load_grammar(), code) return p.module diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index f31542ea..7bee1e64 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -1,15 +1,21 @@ -# python >= 3.2 +""" +Test the typing library, with docstrings. This is needed since annotations +are not supported in python 2.7 else then annotating by comment (and this is +still TODO at 2016-01-23) +""" import typing class B: pass -def we_can_has_sequence( - p: typing.Sequence[int], - q: typing.Sequence[B], - r: "typing.Sequence[int]", - s: typing.Sequence["int"], - t: typing.MutableSequence[dict], - u: typing.List[float]): +def we_can_has_sequence(p, q, r, s, t, u): + """ + :type p: typing.Sequence[int] + :type q: typing.Sequence[B] + :type r: typing.Sequence[int] + :type s: typing.Sequence["int"] + :type t: typing.MutableSequence[dict] + :type u: typing.List[float] + """ #? ["count"] p.c #? int() @@ -37,11 +43,13 @@ def we_can_has_sequence( #? float() u[1] -def iterators( - ps: typing.Iterable[int], - qs: typing.Iterator[str], - rs: typing.Sequence["ForwardReference"], - ts: typing.AbstractSet["float"]): +def iterators(ps, qs, rs, ts): + """ + :type ps: typing.Iterable[int] + :type qs: typing.Iterator[str] + :type rs: typing.Sequence["ForwardReference"] + :type ts: typing.AbstractSet["float"] + """ for p in ps: #? int() p @@ -71,18 +79,22 @@ def iterators( #? float() t -def sets( - p: typing.AbstractSet[int], - q: typing.MutableSet[float]): +def sets(p, q): + """ + :type p: typing.AbstractSet[int] + :type q: typing.MutableSet[float] + """ #? [] p.a #? ["add"] q.a -def tuple( - p: typing.Tuple[int], - q: typing.Tuple[int, str, float], - r: typing.Tuple[B, ...]): +def tuple(p, q, r): + """ + :type p: typing.Tuple[int] + :type q: typing.Tuple[int, str, float] + :type r: typing.Tuple[B, ...] + """ #? int() p[0] #? int() @@ -117,13 +129,15 @@ class Key: class Value: pass -def mapping( - p: typing.Mapping[Key, Value], - q: typing.MutableMapping[Key, Value], - d: typing.Dict[Key, Value], - r: typing.KeysView[Key], - s: typing.ValuesView[Value], - t: typing.ItemsView[Key, Value]): +def mapping(p, q, d, r, s, t): + """ + :type p: typing.Mapping[Key, Value] + :type q: typing.MutableMapping[Key, Value] + :type d: typing.Dict[Key, Value] + :type r: typing.KeysView[Key] + :type s: typing.ValuesView[Value] + :type t: typing.ItemsView[Key, Value] + """ #? [] p.setd #? ["setdefault"] @@ -168,12 +182,14 @@ def mapping( #? Value() value -def union( - p: typing.Union[int], - q: typing.Union[int, int], - r: typing.Union[int, str, "int"], - s: typing.Union[int, typing.Union[str, "typing.Union['float', 'dict']"]], - t: typing.Union[int, None]): +def union(p, q, r, s, t): + """ + :type p: typing.Union[int] + :type q: typing.Union[int, int] + :type r: typing.Union[int, str, "int"] + :type s: typing.Union[int, typing.Union[str, "typing.Union['float', 'dict']"]] + :type t: typing.Union[int, None] + """ #? int() p #? int() @@ -185,9 +201,9 @@ def union( #? int() t -def optional( - p: typing.Optional[int]): +def optional(p): """ + :type p: typing.Optional[int] Optional does not do anything special. However it should be recognised as being of that type. Jedi doesn't do anything with the extra into that it can be None as well @@ -198,26 +214,14 @@ def optional( class ForwardReference: pass -import typing as t -def union2(x: t.Union[int, str]): - #? int() str() - x - -from typing import Union -def union3(x: Union[int, str]): - #? int() str() - x - -from typing import Union as U -def union4(x: U[int, str]): - #? int() str() - x - class TestDict(typing.Dict[str, int]): def setdud(self): pass -def testdict(x: TestDict): +def testdict(x): + """ + :type x: TestDict + """ #? ["setdud", "setdefault"] x.setd for key in x.keys(): @@ -236,3 +240,22 @@ for key in x.keys(): for value in x.values(): #? int() value +# python >= 3.2 +""" +docstrings have some auto-import, annotations can use all of Python's +import logic +""" +import typing as t +def union2(x: t.Union[int, str]): + #? int() str() + x + +from typing import Union +def union3(x: Union[int, str]): + #? int() str() + x + +from typing import Union as U +def union4(x: U[int, str]): + #? int() str() + x From 244c9976e5ebbac163af1f31e5ef517d1851c6d7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 Jan 2016 23:06:28 +0100 Subject: [PATCH 25/26] cache the parsed jedi_typing module --- jedi/evaluate/pep0484.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 6c7f6e8e..08588d71 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -77,18 +77,23 @@ def find_return_types(evaluator, func): return _evaluate_for_annotation(evaluator, annotation) -# TODO: Memoize +_typing_module = None + + def _get_typing_replacement_module(): """ The idea is to return our jedi replacement for the PEP-0484 typing module as discussed at https://github.com/davidhalter/jedi/issues/663 """ - - typing_path = os.path.abspath(os.path.join(__file__, "../jedi_typing.py")) - with open(typing_path) as f: - code = _compatibility.unicode(f.read()) - p = ParserWithRecovery(load_grammar(), code) - return p.module + global _typing_module + if _typing_module is None: + typing_path = \ + os.path.abspath(os.path.join(__file__, "../jedi_typing.py")) + with open(typing_path) as f: + code = _compatibility.unicode(f.read()) + p = ParserWithRecovery(load_grammar(), code) + _typing_module = p.module + return _typing_module def get_types_for_typing_module(evaluator, typ, node): From 079e3bbd28ce360e440f2d6ff36e6955bdb65517 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 Jan 2016 23:09:45 +0100 Subject: [PATCH 26/26] use Ellipsis instead of ..., for python 2.7 compatibility --- jedi/evaluate/jedi_typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jedi/evaluate/jedi_typing.py b/jedi/evaluate/jedi_typing.py index 52b0ab89..f48a5673 100644 --- a/jedi/evaluate/jedi_typing.py +++ b/jedi/evaluate/jedi_typing.py @@ -36,7 +36,7 @@ def factory(typing_name, indextypes): class Tuple(Sequence, tuple): def __getitem__(self, index): - if indextypes[1] == ...: + if indextypes[1] == Ellipsis: # https://www.python.org/dev/peps/pep-0484/#the-typing-module # Tuple[int, ...] means a tuple of ints of indetermined length return indextypes[0]()