diff --git a/README.md b/README.md
index 4011720acdc..2e110bdde86 100644
--- a/README.md
+++ b/README.md
@@ -143,7 +143,7 @@ a Python version greater than 2.7 (Python 3.2 is not supported anymore).
* **Python** 2.7 or 3.3+
* **PyQt5** 5.2+ or **PyQt4** 4.6+: PyQt5 is recommended.
* **qtconsole** 4.2.0+: Enhanced Python interpreter.
-* **Rope** and **Jedi** 0.9.0: Editor code completion, calltips
+* **Rope** and **Jedi**: Editor code completion, calltips
and go-to-definition.
* **Pyflakes**: Real-time code analysis.
* **Sphinx**: Rich text mode for the Help pane.
diff --git a/continuous_integration/circle/install.sh b/continuous_integration/circle/install.sh
index f3564d5579c..c42b12e9d0f 100755
--- a/continuous_integration/circle/install.sh
+++ b/continuous_integration/circle/install.sh
@@ -7,10 +7,10 @@ if [ "$CIRCLE_NODE_INDEX" = "3" ]; then
export CONDA_DEPENDENCIES=""
else
export CONDA_DEPENDENCIES_FLAGS="--quiet"
- export CONDA_DEPENDENCIES="rope jedi pyflakes sphinx pygments pylint pep8 psutil nbconvert \
+ export CONDA_DEPENDENCIES="rope pyflakes sphinx pygments pylint pep8 psutil nbconvert \
qtawesome pickleshare qtpy pyzmq chardet mock nomkl pandas \
pytest pytest-cov numpydoc scipy"
- export PIP_DEPENDENCIES="coveralls pytest-qt pytest-xvfb flaky"
+ export PIP_DEPENDENCIES="coveralls pytest-qt pytest-xvfb flaky jedi"
fi
diff --git a/continuous_integration/conda-recipes/spyder/meta.yaml b/continuous_integration/conda-recipes/spyder/meta.yaml
index 41f90cbaa8e..e0e971dbab8 100644
--- a/continuous_integration/conda-recipes/spyder/meta.yaml
+++ b/continuous_integration/conda-recipes/spyder/meta.yaml
@@ -23,7 +23,7 @@ requirements:
- rope 0.9.* # [py34 or py35]
- rope # [py27]
- pyflakes
- - jedi 0.9.*
+ - jedi
- qtconsole
- nbconvert
- pygments
diff --git a/doc/installation.rst b/doc/installation.rst
index 678ba8f381a..9b31fd78659 100644
--- a/doc/installation.rst
+++ b/doc/installation.rst
@@ -161,7 +161,7 @@ The requirements to run Spyder are:
enhanced Python interpreter.
* `Rope `_ >=0.9.4 and
- `Jedi ` 0.9.0 -- for code completion,
+ `Jedi `_ >=0.9.0 -- for code completion,
go-to-definition and calltips on the Editor.
* `Pyflakes `_ -- for real-time
diff --git a/setup.py b/setup.py
index 9fe1c5d29d0..60d6d4a4679 100644
--- a/setup.py
+++ b/setup.py
@@ -272,7 +272,7 @@ def run(self):
install_requires = [
'rope_py3k' if PY3 else 'rope>=0.9.4',
- 'jedi==0.9.0',
+ 'jedi>=0.9.0',
'pyflakes',
'pygments>=2.0',
'qtconsole>=4.2.0',
diff --git a/spyder/utils/introspection/jedi_patch.py b/spyder/utils/introspection/jedi_patch.py
index 1790be10d7c..d6cf23cfdb6 100644
--- a/spyder/utils/introspection/jedi_patch.py
+++ b/spyder/utils/introspection/jedi_patch.py
@@ -22,10 +22,11 @@ def apply():
See [1] and [2] module docstring."""
from spyder.utils.programs import is_module_installed
- if is_module_installed('jedi', '=0.9.0'):
+ if (is_module_installed('jedi', '=0.9.0') or
+ is_module_installed('jedi', '=0.10.0')):
import jedi
else:
- raise ImportError("jedi %s can't be patched" % jedi.__version__)
+ raise ImportError("jedi not =0.9.0 or 0.10.0, can't be patched")
# [1] Adding numpydoc type returns to docstrings
from spyder.utils.introspection import numpy_docstr
@@ -36,11 +37,10 @@ def apply():
# [2] Adding type returns for compiled objects in jedi
# Patching jedi.evaluate.compiled.CompiledObject...
- from jedi.evaluate.compiled import (
- CompiledObject, builtin, _create_from_name, debug)
+ if is_module_installed('jedi', '=0.9.0'):
+ from jedi.evaluate.compiled import (builtin, _create_from_name,
+ debug, CompiledObject)
- class CompiledObject(CompiledObject):
- # ...adding docstrings int _execute_function...
def _execute_function(self, evaluator, params):
if self.type != 'funcdef':
return
@@ -58,87 +58,110 @@ def _execute_function(self, evaluator, params):
except AttributeError:
continue
else:
- if isinstance(bltn_obj, CompiledObject) and bltn_obj.obj is None:
+ if (isinstance(bltn_obj, CompiledObject) and
+ bltn_obj.obj is None):
# We want everything except None.
continue
for result in evaluator.execute(bltn_obj, params):
yield result
- # ...docstrings needs a raw_doc property
- @property
- def raw_doc(self):
- try:
- doc = unicode(self.doc)
- except NameError: # python 3
- doc = self.doc
- return doc
-
- jedi.evaluate.compiled.CompiledObject = CompiledObject
-
- # [3] Fixing introspection for matplotlib Axes objects
- # Patching jedi.evaluate.precedence...
- from jedi.evaluate.precedence import tree, calculate
-
- def calculate_children(evaluator, children):
- """
- Calculate a list of children with operators.
- """
- iterator = iter(children)
- types = evaluator.eval_element(next(iterator))
- for operator in iterator:
- try:# PATCH: Catches StopIteration error
- right = next(iterator)
- if tree.is_node(operator, 'comp_op'): # not in / is not
- operator = ' '.join(str(c.value) for c in operator.children)
-
- # handle lazy evaluation of and/or here.
- if operator in ('and', 'or'):
- left_bools = set([left.py__bool__() for left in types])
- if left_bools == set([True]):
- if operator == 'and':
- types = evaluator.eval_element(right)
- elif left_bools == set([False]):
- if operator != 'and':
- types = evaluator.eval_element(right)
- # Otherwise continue, because of uncertainty.
+ else: # Code for Jedi 0.10.0
+ from jedi.evaluate.compiled import debug, create
+ from jedi._compatibility import builtins as _builtins
+
+ def _execute_function(self, params):
+ from spyder.utils.introspection import numpy_docstr
+ if self.type != 'funcdef':
+ return
+ types = set([])
+ types |= set(numpy_docstr.find_return_types(self.parent_context,
+ self))
+ debug.dbg('docstrings type return: %s in %s', types, self)
+ for name in self._parse_function_doc()[1].split():
+ try:
+ bltn_obj = getattr(_builtins, name)
+ except AttributeError:
+ continue
else:
- types = calculate(evaluator, types, operator,
- evaluator.eval_element(right))
- except StopIteration:
- debug.warning('calculate_children StopIteration %s', types)
- debug.dbg('calculate_children types %s', types)
- return types
-
- jedi.evaluate.precedence.calculate_children = calculate_children
-
- # [4] Fixing introspection for matplotlib Axes objects
- # Patching jedi.evaluate.precedence...
- from jedi.evaluate.representation import (
- InstanceName, Instance, compiled, FunctionExecution, InstanceElement)
-
- def get_instance_el(evaluator, instance, var, is_class_var=False):
- """
- Returns an InstanceElement if it makes sense, otherwise leaves the object
- untouched.
-
- Basically having an InstanceElement is context information. That is needed
- in quite a lot of cases, which includes Nodes like ``power``, that need to
- know where a self name comes from for example.
- """
- if isinstance(var, tree.Name):
- parent = get_instance_el(evaluator, instance, var.parent, is_class_var)
- return InstanceName(var, parent)
- # PATCH: compiled objects can be None
- elif var is None:
- return var
- elif var.type != 'funcdef' \
- and isinstance(var, (Instance, compiled.CompiledObject, tree.Leaf,
- tree.Module, FunctionExecution)):
- return var
-
- var = evaluator.wrap(var)
- return InstanceElement(evaluator, instance, var, is_class_var)
-
- jedi.evaluate.representation.get_instance_el = get_instance_el
+ if bltn_obj is None:
+ # We want to evaluate everything except None.
+ continue
+ bltn_obj = create(self.evaluator, bltn_obj)
+ types |= set(self.evaluator.execute(bltn_obj, params))
+ for result in types:
+ yield result
+
+ jedi.evaluate.compiled.CompiledObject._execute_function = _execute_function
+
+ if is_module_installed('jedi', '=0.9.0'):
+ # [3] Fixing introspection for matplotlib Axes objects
+ # Patching jedi.evaluate.precedence...
+ from jedi.evaluate.precedence import tree, calculate
+
+ def calculate_children(evaluator, children):
+ """
+ Calculate a list of children with operators.
+ """
+ iterator = iter(children)
+ types = evaluator.eval_element(next(iterator))
+ for operator in iterator:
+ try: # PATCH: Catches StopIteration error
+ right = next(iterator)
+ if tree.is_node(operator, 'comp_op'): # not in / is not
+ operator = ' '.join(str(c.value) for c in
+ operator.children)
+
+ # handle lazy evaluation of and/or here.
+ if operator in ('and', 'or'):
+ left_bools = set([left.py__bool__() for left in types])
+ if left_bools == set([True]):
+ if operator == 'and':
+ types = evaluator.eval_element(right)
+ elif left_bools == set([False]):
+ if operator != 'and':
+ types = evaluator.eval_element(right)
+ # Otherwise continue, because of uncertainty.
+ else:
+ types = calculate(evaluator, types, operator,
+ evaluator.eval_element(right))
+ except StopIteration:
+ debug.warning('calculate_children StopIteration %s', types)
+ debug.dbg('calculate_children types %s', types)
+ return types
+
+ jedi.evaluate.precedence.calculate_children = calculate_children
+
+ # [4] Fixing introspection for matplotlib Axes objects
+ # Patching jedi.evaluate.precedence...
+ from jedi.evaluate.representation import (InstanceName, Instance,
+ compiled, FunctionExecution,
+ InstanceElement)
+
+ def get_instance_el(evaluator, instance, var, is_class_var=False):
+ """
+ Returns an InstanceElement if it makes sense, otherwise leaves the
+ object untouched.
+
+ Basically having an InstanceElement is context information. That is
+ needed in quite a lot of cases, which includes Nodes like
+ ``power``, that need to know where a self name comes from for
+ example.
+ """
+ if isinstance(var, tree.Name):
+ parent = get_instance_el(evaluator, instance, var.parent,
+ is_class_var)
+ return InstanceName(var, parent)
+ # PATCH: compiled objects can be None
+ elif var is None:
+ return var
+ elif var.type != 'funcdef' \
+ and isinstance(var, (Instance, compiled.CompiledObject,
+ tree.Leaf, tree.Module, FunctionExecution)):
+ return var
+
+ var = evaluator.wrap(var)
+ return InstanceElement(evaluator, instance, var, is_class_var)
+
+ jedi.evaluate.representation.get_instance_el = get_instance_el
return jedi
diff --git a/spyder/utils/introspection/jedi_plugin.py b/spyder/utils/introspection/jedi_plugin.py
index c66d5e8afbb..1ff5a7da66e 100644
--- a/spyder/utils/introspection/jedi_plugin.py
+++ b/spyder/utils/introspection/jedi_plugin.py
@@ -54,7 +54,6 @@ def get_completions(self, info):
completions = self.get_jedi_object('completions', info)
if DEBUG_EDITOR:
log_last_error(LOG_FILENAME, str("comp: " + str(completions)[:100]))
- debug_print("comp: " + str(completions)[:100])
completions = [(c.name, c.type) for c in completions]
debug_print(str(completions)[:100])
return completions
diff --git a/spyder/utils/introspection/manager.py b/spyder/utils/introspection/manager.py
index eb5df245754..6eb0e1a9c79 100644
--- a/spyder/utils/introspection/manager.py
+++ b/spyder/utils/introspection/manager.py
@@ -33,7 +33,7 @@
_("Editor's code completion, go-to-definition and help"),
required_version=ROPE_REQVER)
-JEDI_REQVER = '=0.9.0'
+JEDI_REQVER = '>=0.9.0'
dependencies.add('jedi',
_("Editor's code completion, go-to-definition and help"),
required_version=JEDI_REQVER)
diff --git a/spyder/utils/introspection/numpy_docstr.py b/spyder/utils/introspection/numpy_docstr.py
index c3ff0e83c1d..08432459c00 100644
--- a/spyder/utils/introspection/numpy_docstr.py
+++ b/spyder/utils/introspection/numpy_docstr.py
@@ -15,7 +15,9 @@
from ast import literal_eval
import re
-from jedi._compatibility import is_py3
+from spyder.utils.programs import is_module_installed
+
+from jedi._compatibility import u, is_py3
from jedi.evaluate.cache import memoize_default
from jedi.evaluate.docstrings import (_evaluate_for_statement_string,
_strip_rst_role,
@@ -105,9 +107,9 @@ def _search_return_in_numpydocstr(docstr):
found.extend(_expand_typestr(p_type))
return found
-
-@memoize_default(None, evaluator_is_first_arg=True)
-def find_return_types(evaluator, func):
+# Caching disabled because jedi_patch breaks it
+# @memoize_default(None, evaluator_is_first_arg=True)
+def find_return_types(module_context, func):
"""
Determines a set of potential return types for `func` using docstring hints
:type evaluator: jedi.evaluate.Evaluator
@@ -140,11 +142,17 @@ def search_return_in_docstr(docstr):
# Check for numpy style return hint
found = _search_return_in_numpydocstr(docstr)
return found
-
- docstr = func.raw_doc
- module = func.get_parent_until()
+ try:
+ docstr = u(func.raw_doc)
+ except AttributeError:
+ docstr = u(func.doc)
types = []
for type_str in search_return_in_docstr(docstr):
- type_ = _evaluate_for_statement_string(evaluator, type_str, module)
+ if is_module_installed('jedi', '=0.10.0'):
+ type_ = _evaluate_for_statement_string(module_context, type_str)
+ else:
+ module = func.get_parent_until()
+ type_ = _evaluate_for_statement_string(module_context,
+ type_str, module)
types.extend(type_)
return types
diff --git a/spyder/utils/introspection/test/test_jedi_plugin.py b/spyder/utils/introspection/test/test_jedi_plugin.py
index eb8e870939d..5c5395961d8 100644
--- a/spyder/utils/introspection/test/test_jedi_plugin.py
+++ b/spyder/utils/introspection/test/test_jedi_plugin.py
@@ -32,7 +32,7 @@
def test_get_info():
- source_code = "import os; os.walk("
+ source_code = "import os; os.walk"
docs = p.get_info(CodeInfo('info', source_code, len(source_code)))
assert docs['calltip'].startswith('walk(') and docs['name'] == 'walk'
@@ -63,7 +63,7 @@ def test_get_docstring():
def test(a, b):
"""Test docstring"""
pass
- test(1,''')
+ test''')
path, line = p.get_definition(CodeInfo('definition', source_code,
len(source_code), 'dummy.txt',
is_python_like=True))