Skip to content

Commit

Permalink
Merge from 3.1.x: PR #4121
Browse files Browse the repository at this point in the history
  • Loading branch information
ccordoba12 committed Mar 13, 2017
2 parents 235d396 + 1ba423f commit 64c39ab
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 101 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions continuous_integration/circle/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 cython"
export PIP_DEPENDENCIES="coveralls pytest-qt pytest-xvfb flaky"
export PIP_DEPENDENCIES="coveralls pytest-qt pytest-xvfb flaky jedi"
fi


Expand Down
2 changes: 1 addition & 1 deletion continuous_integration/conda-recipes/spyder/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ requirements:
- rope 0.9.* # [py34 or py35]
- rope # [py27]
- pyflakes
- jedi 0.9.*
- jedi
- qtconsole
- nbconvert
- pygments
Expand Down
2 changes: 1 addition & 1 deletion doc/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
187 changes: 105 additions & 82 deletions spyder/utils/introspection/jedi_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
1 change: 0 additions & 1 deletion spyder/utils/introspection/jedi_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion spyder/utils/introspection/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
24 changes: 16 additions & 8 deletions spyder/utils/introspection/numpy_docstr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
4 changes: 2 additions & 2 deletions spyder/utils/introspection/test/test_jedi_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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))
Expand Down
1 change: 0 additions & 1 deletion spyder/utils/iofuncs.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,4 +490,3 @@ def save_auto(data, filename):
os.remove("test.spydata")

print("Data loaded in %.3f seconds" % (time.time()-t0)) # spyder: test-skip
os.remove("test.spydata")

0 comments on commit 64c39ab

Please sign in to comment.