[spyder] 04/05: Cherry-pick upstream fix for broken autocompletion
Ghislain Vaillant
ghisvail-guest at moszumanska.debian.org
Sat Apr 29 10:46:31 UTC 2017
This is an automated email from the git hooks/post-receive script.
ghisvail-guest pushed a commit to branch experimental
in repository spyder.
commit f3a5f0acae2743ec0f69a7e0540ed6e84b7cff16
Author: Ghislain Antony Vaillant <ghisvail at gmail.com>
Date: Thu Mar 16 09:35:40 2017 +0000
Cherry-pick upstream fix for broken autocompletion
Gbp-Dch: short
Closes: #855873
Thanks: bcolsen for fixing the issue upstream
---
.../Fix-autocompletion-with-Jedi-0.10.patch | 380 +++++++++++++++++++++
debian/patches/series | 1 +
2 files changed, 381 insertions(+)
diff --git a/debian/patches/Fix-autocompletion-with-Jedi-0.10.patch b/debian/patches/Fix-autocompletion-with-Jedi-0.10.patch
new file mode 100644
index 0000000..54e838e
--- /dev/null
+++ b/debian/patches/Fix-autocompletion-with-Jedi-0.10.patch
@@ -0,0 +1,380 @@
+From: bcolsen <bcolsen at gmail.com>
+Date: Wed, 8 Feb 2017 00:53:21 -0700
+Subject: Fix autocompletion with Jedi 0.10
+
+This is a combination of 7 commits:
+
+* [af0d270] Numpydoc monkey patch for jedi 0.10.0
+* [d233969] Code clean up and PEP8
+* [a252d1c] Remove jedi 0.9.0 pinning
+* [113f5ef] cache fix
+* [f9fe24d] disable cache
+* [4e6a7f6] Change the tests?
+* [c34b99d] Docs: Fix link to Jedi webpage
+---
+ README.md | 2 +-
+ doc/installation.rst | 2 +-
+ setup.py | 2 +-
+ spyder/utils/introspection/jedi_patch.py | 187 ++++++++++++---------
+ spyder/utils/introspection/jedi_plugin.py | 1 -
+ spyder/utils/introspection/manager.py | 2 +-
+ spyder/utils/introspection/numpy_docstr.py | 24 ++-
+ .../utils/introspection/test/test_jedi_plugin.py | 4 +-
+ 8 files changed, 127 insertions(+), 97 deletions(-)
+
+diff --git a/README.md b/README.md
+index 4011720..2e110bd 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/doc/installation.rst b/doc/installation.rst
+index 678ba8f..9b31fd7 100644
+--- a/doc/installation.rst
++++ b/doc/installation.rst
+@@ -161,7 +161,7 @@ The requirements to run Spyder are:
+ enhanced Python interpreter.
+
+ * `Rope <http://rope.sourceforge.net/>`_ >=0.9.4 and
+- `Jedi <http://jedi.jedidjah.ch/en/latest/>` 0.9.0 -- for code completion,
++ `Jedi <http://jedi.jedidjah.ch/en/latest/>`_ >=0.9.0 -- for code completion,
+ go-to-definition and calltips on the Editor.
+
+ * `Pyflakes <http://pypi.python.org/pypi/pyflakes>`_ -- for real-time
+diff --git a/setup.py b/setup.py
+index 3ec0af3..b46fc87 100644
+--- a/setup.py
++++ b/setup.py
+@@ -271,7 +271,7 @@ if any(arg == 'bdist_wheel' for arg in sys.argv):
+
+ 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 1790be1..d6cf23c 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 apply():
+ 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 c66d5e8..1ff5a7d 100644
+--- a/spyder/utils/introspection/jedi_plugin.py
++++ b/spyder/utils/introspection/jedi_plugin.py
+@@ -54,7 +54,6 @@ class JediPlugin(IntrospectionPlugin):
+ 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 eb5df24..6eb0e1a 100644
+--- a/spyder/utils/introspection/manager.py
++++ b/spyder/utils/introspection/manager.py
+@@ -33,7 +33,7 @@ dependencies.add('rope',
+ _("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 c3ff0e8..0843245 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
+
+-
+- at 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 find_return_types(evaluator, func):
+ # 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 eb8e870..5c53959 100644
+--- a/spyder/utils/introspection/test/test_jedi_plugin.py
++++ b/spyder/utils/introspection/test/test_jedi_plugin.py
+@@ -32,7 +32,7 @@ p.load_plugin()
+
+
+ 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))
diff --git a/debian/patches/series b/debian/patches/series
index ba8e634..b23c4da 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1 +1,2 @@
0001-fix-spyderlib-path.patch
+Fix-autocompletion-with-Jedi-0.10.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/spyder.git
More information about the debian-science-commits
mailing list