[spyder] 01/02: Cherry-pick upstream fix for broken autocompletion

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Thu Mar 16 12:12:11 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 8ef2a2db2040bfd6eabed96a02f90cad0a34ef71
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