Merge with master

The deprecation of Python2.6 and the insertion of environments made it quite difficult to merge.
This commit is contained in:
Dave Halter
2018-01-20 18:58:43 +01:00
48 changed files with 205 additions and 210 deletions

1
.gitignore vendored
View File

@@ -5,6 +5,7 @@
.tox .tox
.coveralls.yml .coveralls.yml
.coverage .coverage
.idea
/build/ /build/
/docs/_build/ /docs/_build/
/dist/ /dist/

View File

@@ -3,6 +3,12 @@
Changelog Changelog
--------- ---------
0.12.0 (2018-01-01)
+++++++++++++++++++
- Remove Python 2.6
- VirtualEnvs
0.11.1 (2017-12-14) 0.11.1 (2017-12-14)
+++++++++++++++++++ +++++++++++++++++++

View File

@@ -2,16 +2,23 @@
Jedi - an awesome autocompletion/static analysis library for Python Jedi - an awesome autocompletion/static analysis library for Python
################################################################### ###################################################################
.. image:: https://secure.travis-ci.org/davidhalter/jedi.png?branch=master .. image:: https://img.shields.io/pypi/v/jedi.svg?style=flat
:target: http://travis-ci.org/davidhalter/jedi :target: https://pypi.python.org/pypi/jedi
:alt: Travis-CI build status :alt: PyPI version
.. image:: https://coveralls.io/repos/davidhalter/jedi/badge.png?branch=master .. image:: https://img.shields.io/pypi/pyversions/jedi.svg
:target: https://pypi.python.org/pypi/jedi
:alt: Supported Python versions
.. image:: https://travis-ci.org/davidhalter/jedi.svg?branch=master
:target: https://travis-ci.org/davidhalter/jedi
:alt: Travis CI build status
.. image:: https://coveralls.io/repos/davidhalter/jedi/badge.svg?branch=master
:target: https://coveralls.io/r/davidhalter/jedi :target: https://coveralls.io/r/davidhalter/jedi
:alt: Coverage Status :alt: Coverage status
*If you have specific questions, please add an issue or ask on* `Stack Overflow
*If you have specific questions, please add an issue or ask on* `stackoverflow
<https://stackoverflow.com/questions/tagged/python-jedi>`_ *with the label* ``python-jedi``. <https://stackoverflow.com/questions/tagged/python-jedi>`_ *with the label* ``python-jedi``.
@@ -25,7 +32,7 @@ related names and to list all names in a Python file and infer them. Jedi
understands docstrings and you can use Jedi autocompletion in your REPL as understands docstrings and you can use Jedi autocompletion in your REPL as
well. well.
Jedi uses a very simple API to connect with IDE's. There's a reference Jedi uses a very simple API to connect with IDEs. There's a reference
implementation as a `VIM-Plugin <https://github.com/davidhalter/jedi-vim>`_, implementation as a `VIM-Plugin <https://github.com/davidhalter/jedi-vim>`_,
which uses Jedi's autocompletion. We encourage you to use Jedi in your IDEs. which uses Jedi's autocompletion. We encourage you to use Jedi in your IDEs.
It's really easy. It's really easy.
@@ -45,7 +52,7 @@ Jedi can currently be used with the following editors/projects:
- Gedit (gedi_) - Gedit (gedi_)
- wdb_ - Web Debugger - wdb_ - Web Debugger
- `Eric IDE`_ (Available as a plugin) - `Eric IDE`_ (Available as a plugin)
- `Ipython 6.0.0+ <https://ipython.readthedocs.io/en/stable/whatsnew/version6.html>`_ - `IPython 6.0.0+ <https://ipython.readthedocs.io/en/stable/whatsnew/version6.html>`_
and many more! and many more!
@@ -96,7 +103,7 @@ understands, see: `Features
<https://jedi.readthedocs.org/en/latest/docs/features.html>`_. A list of <https://jedi.readthedocs.org/en/latest/docs/features.html>`_. A list of
caveats can be found on the same page. caveats can be found on the same page.
You can run Jedi on cPython 2.6, 2.7, 3.3, 3.4 or 3.5 but it should also You can run Jedi on CPython 2.7 or 3.3+ but it should also
understand/parse code older than those versions. understand/parse code older than those versions.
Tips on how to use Jedi efficiently can be found `here Tips on how to use Jedi efficiently can be found `here
@@ -123,7 +130,7 @@ The returned objects are very powerful and really all you might need.
Autocompletion in your REPL (IPython, etc.) Autocompletion in your REPL (IPython, etc.)
------------------------------------------- -------------------------------------------
Starting with Ipython `6.0.0` Jedi is a dependency of IPython. Autocompletion Starting with IPython `6.0.0` Jedi is a dependency of IPython. Autocompletion
in IPython is therefore possible without additional configuration. in IPython is therefore possible without additional configuration.
It's possible to have Jedi autocompletion in REPL modes - `example video <https://vimeo.com/122332037>`_. It's possible to have Jedi autocompletion in REPL modes - `example video <https://vimeo.com/122332037>`_.
@@ -178,7 +185,7 @@ Tests are also run automatically on `Travis CI
<https://travis-ci.org/davidhalter/jedi/>`_. <https://travis-ci.org/davidhalter/jedi/>`_.
For more detailed information visit the `testing documentation For more detailed information visit the `testing documentation
<https://jedi.readthedocs.org/en/latest/docs/testing.html>`_ <https://jedi.readthedocs.org/en/latest/docs/testing.html>`_.
Acknowledgements Acknowledgements

View File

@@ -19,7 +19,6 @@
{% endblock %} {% endblock %}
{%- block footer %} {%- block footer %}
<div class="footer"> <div class="footer">
&copy; Copyright {{ copyright }}.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
</div> </div>
{% if pagename == 'index' %} {% if pagename == 'index' %}

View File

@@ -45,7 +45,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Jedi' project = u'Jedi'
copyright = u'2012 - {today.year}, Jedi contributors'.format(today=datetime.date.today()) copyright = u'jedi contributors'
import jedi import jedi
from jedi.utils import version_info from jedi.utils import version_info

View File

@@ -20,7 +20,7 @@ make it work.
General Features General Features
---------------- ----------------
- python 2.6+ and 3.3+ support - Python 2.7 and 3.3+ support
- ignores syntax errors and wrong indentation - ignores syntax errors and wrong indentation
- can deal with complex module / function / class structures - can deal with complex module / function / class structures
- virtualenv support - virtualenv support
@@ -64,7 +64,7 @@ Not yet implemented:
- manipulations of instances outside the instance variables without using - manipulations of instances outside the instance variables without using
methods methods
- implicit namespace packages (Python 3.3+, `PEP 420 <https://www.python.org/dev/peps/pep-0420/>`_) - implicit namespace packages (Python 3.4+, `PEP 420 <https://www.python.org/dev/peps/pep-0420/>`_)
Will probably never be implemented: Will probably never be implemented:
@@ -88,7 +88,7 @@ etc.
**Security** **Security**
Security is an important issue for |jedi|. Therefore no Python code is Security is an important issue for |jedi|. Therefore no Python code is
executed. As long as you write pure python, everything is evaluated executed. As long as you write pure Python, everything is evaluated
statically. But: If you use builtin modules (``c_builtin``) there is no other statically. But: If you use builtin modules (``c_builtin``) there is no other
option than to execute those modules. However: Execute isn't that critical (as option than to execute those modules. However: Execute isn't that critical (as
e.g. in pythoncomplete, which used to execute *every* import!), because it e.g. in pythoncomplete, which used to execute *every* import!), because it
@@ -117,7 +117,7 @@ one of the following docstring/annotation syntax styles:
https://www.python.org/dev/peps/pep-0484/ https://www.python.org/dev/peps/pep-0484/
function annotations (python 3 only; python 2 function annotations with function annotations (Python 3 only; Python 2 function annotations with
comments in planned but not yet implemented) comments in planned but not yet implemented)
:: ::
@@ -129,7 +129,7 @@ comments in planned but not yet implemented)
node.| # complete here node.| # complete here
assignment, for-loop and with-statement type hints (all python versions). assignment, for-loop and with-statement type hints (all Python versions).
Note that the type hints must be on the same line as the statement Note that the type hints must be on the same line as the statement
:: ::
@@ -142,7 +142,7 @@ Note that the type hints must be on the same line as the statement
print(f + 3) print(f + 3)
Most of the features in PEP-0484 are supported including the typing module Most of the features in PEP-0484 are supported including the typing module
(for python < 3.5 you have to do ``pip install typing`` to use these), (for Python < 3.5 you have to do ``pip install typing`` to use these),
and forward references. and forward references.
Things that are missing (and this is not an exhaustive list; some of these Things that are missing (and this is not an exhaustive list; some of these

View File

@@ -1,5 +1,5 @@
""" """
To ensure compatibility from Python ``2.6`` - ``3.3``, a module has been To ensure compatibility from Python ``2.7`` - ``3.x``, a module has been
created. Clearly there is huge need to use conforming syntax. created. Clearly there is huge need to use conforming syntax.
""" """
import sys import sys
@@ -13,12 +13,10 @@ try:
except ImportError: except ImportError:
pass pass
# Cannot use sys.version.major and minor names, because in Python 2.6 it's not
# a namedtuple.
is_py3 = sys.version_info[0] >= 3 is_py3 = sys.version_info[0] >= 3
is_py33 = is_py3 and sys.version_info[1] >= 3 is_py33 = is_py3 and sys.version_info[1] >= 3
is_py34 = is_py3 and sys.version_info[1] >= 4 is_py34 = is_py3 and sys.version_info[1] >= 4
is_py26 = not is_py3 and sys.version_info[1] < 7 is_py35 = is_py3 and sys.version_info[1] >= 5
py_version = int(str(sys.version_info[0]) + str(sys.version_info[1])) py_version = int(str(sys.version_info[0]) + str(sys.version_info[1]))
@@ -74,7 +72,7 @@ def find_module_py33(string, path=None, loader=None, full_name=None):
raise ImportError("Originally " + repr(e)) raise ImportError("Originally " + repr(e))
if loader is None: if loader is None:
raise ImportError("Couldn't find a loader for {0}".format(string)) raise ImportError("Couldn't find a loader for {}".format(string))
try: try:
is_package = loader.is_package(string) is_package = loader.is_package(string)
@@ -127,14 +125,7 @@ def find_module_pre_py33(string, path=None, full_name=None):
if loader: if loader:
is_package = loader.is_package(string) is_package = loader.is_package(string)
is_archive = hasattr(loader, 'archive') is_archive = hasattr(loader, 'archive')
try:
module_path = loader.get_filename(string) module_path = loader.get_filename(string)
except AttributeError:
# fallback for py26
try:
module_path = loader._get_filename(string)
except AttributeError:
continue
if is_package: if is_package:
module_path = os.path.dirname(module_path) module_path = os.path.dirname(module_path)
if is_archive: if is_archive:
@@ -142,10 +133,10 @@ def find_module_pre_py33(string, path=None, full_name=None):
file = None file = None
if not is_package or is_archive: if not is_package or is_archive:
file = DummyFile(loader, string) file = DummyFile(loader, string)
return (file, module_path, is_package) return file, module_path, is_package
except ImportError: except ImportError:
pass pass
raise ImportError("No module named {0}".format(string)) raise ImportError("No module named {}".format(string))
find_module = find_module_py33 if is_py33 else find_module_pre_py33 find_module = find_module_py33 if is_py33 else find_module_pre_py33
@@ -260,11 +251,6 @@ import ast
def literal_eval(string): def literal_eval(string):
# py3.0, py3.1 and py32 don't support unicode literals. Support those, I
# don't want to write two versions of the tokenizer.
if is_py3 and sys.version_info.minor < 3:
if re.match('[uU][\'"]', string):
string = string[1:]
return ast.literal_eval(string) return ast.literal_eval(string)

View File

@@ -12,8 +12,8 @@ arguments.
import os import os
import sys import sys
import parso
from parso.python import tree from parso.python import tree
from parso import python_bytes_to_unicode, split_lines
from jedi._compatibility import force_unicode, is_py3 from jedi._compatibility import force_unicode, is_py3
from jedi.parser_utils import get_executable_nodes from jedi.parser_utils import get_executable_nodes
@@ -94,9 +94,29 @@ class Script(object):
with open(path, 'rb') as f: with open(path, 'rb') as f:
source = f.read() source = f.read()
# TODO do we really want that? # Load the Python grammar of the current interpreter.
self._source = python_bytes_to_unicode(source, encoding, errors='replace') self._grammar = parso.load_grammar()
self._code_lines = split_lines(self._source)
if sys_path is not None and not is_py3:
sys_path = list(map(force_unicode, sys_path))
# Load the Python grammar of the current interpreter.
project = get_default_project()
# TODO deprecate and remove sys_path from the Script API.
if sys_path is not None:
project._sys_path = sys_path
self._evaluator = Evaluator(project, environment=environment, script_path=path)
self._project = project
debug.speed('init')
self._module_node, source = self._evaluator.parse_and_get_code(
code=source,
path=self.path,
cache=False, # No disk cache, because the current script often changes.
diff_cache=True,
cache_path=settings.cache_directory
)
debug.speed('parsed')
self._code_lines = parso.split_lines(source)
line = max(len(self._code_lines), 1) if line is None else line line = max(len(self._code_lines), 1) if line is None else line
if not (0 < line <= len(self._code_lines)): if not (0 < line <= len(self._code_lines)):
raise ValueError('`line` parameter is not in a valid range.') raise ValueError('`line` parameter is not in a valid range.')
@@ -111,35 +131,8 @@ class Script(object):
cache.clear_time_caches() cache.clear_time_caches()
debug.reset_time() debug.reset_time()
if sys_path is not None and not is_py3:
sys_path = list(map(force_unicode, sys_path))
# Load the Python grammar of the current interpreter.
project = get_default_project()
# TODO deprecate and remove sys_path from the Script API.
if sys_path is not None:
project._sys_path = sys_path
self._evaluator = Evaluator(project, environment=environment, script_path=path)
self._project = project
debug.speed('init')
@cache.memoize_method
def _get_module_node(self):
return self._evaluator.grammar.parse(
code=self._source,
path=self.path,
cache=False, # No disk cache, because the current script often changes.
diff_cache=True,
cache_path=settings.cache_directory
)
@cache.memoize_method
def _get_module(self): def _get_module(self):
module = ModuleContext( module = ModuleContext(self._evaluator, self._module_node, self.path)
self._evaluator,
self._get_module_node(),
self.path
)
if self.path is not None: if self.path is not None:
name = dotted_path_in_sys_path(self._evaluator.get_sys_path(), self.path) name = dotted_path_in_sys_path(self._evaluator.get_sys_path(), self.path)
if name is not None: if name is not None:
@@ -178,10 +171,9 @@ class Script(object):
:rtype: list of :class:`classes.Definition` :rtype: list of :class:`classes.Definition`
""" """
module_node = self._get_module_node() leaf = self._module_node.get_name_of_position(self._pos)
leaf = module_node.get_name_of_position(self._pos)
if leaf is None: if leaf is None:
leaf = module_node.get_leaf_for_position(self._pos) leaf = self._module_node.get_leaf_for_position(self._pos)
if leaf is None: if leaf is None:
return [] return []
@@ -212,7 +204,7 @@ class Script(object):
else: else:
yield name yield name
tree_name = self._get_module_node().get_name_of_position(self._pos) tree_name = self._module_node.get_name_of_position(self._pos)
if tree_name is None: if tree_name is None:
return [] return []
context = self._evaluator.create_context(self._get_module(), tree_name) context = self._evaluator.create_context(self._get_module(), tree_name)
@@ -243,7 +235,7 @@ class Script(object):
:rtype: list of :class:`classes.Definition` :rtype: list of :class:`classes.Definition`
""" """
tree_name = self._get_module_node().get_name_of_position(self._pos) tree_name = self._module_node.get_name_of_position(self._pos)
if tree_name is None: if tree_name is None:
# Must be syntax # Must be syntax
return [] return []
@@ -270,7 +262,7 @@ class Script(object):
:rtype: list of :class:`classes.CallSignature` :rtype: list of :class:`classes.CallSignature`
""" """
call_signature_details = \ call_signature_details = \
helpers.get_call_signature_details(self._get_module_node(), self._pos) helpers.get_call_signature_details(self._module_node, self._pos)
if call_signature_details is None: if call_signature_details is None:
return [] return []
@@ -295,10 +287,9 @@ class Script(object):
def _analysis(self): def _analysis(self):
self._evaluator.is_analysis = True self._evaluator.is_analysis = True
module_node = self._get_module_node() self._evaluator.analysis_modules = [self._module_node]
self._evaluator.analysis_modules = [module_node]
try: try:
for node in get_executable_nodes(module_node): for node in get_executable_nodes(self._module_node):
context = self._get_module().create_context(node) context = self._get_module().create_context(node)
if node.type in ('funcdef', 'classdef'): if node.type in ('funcdef', 'classdef'):
# Resolve the decorators. # Resolve the decorators.
@@ -374,10 +365,9 @@ class Interpreter(Script):
self.namespaces = namespaces self.namespaces = namespaces
def _get_module(self): def _get_module(self):
parser_module = super(Interpreter, self)._get_module_node()
return interpreter.MixedModuleContext( return interpreter.MixedModuleContext(
self._evaluator, self._evaluator,
parser_module, self._module_node,
self.namespaces, self.namespaces,
path=self.path path=self.path
) )
@@ -413,7 +403,7 @@ def names(source=None, path=None, encoding='utf-8', all_scopes=False,
module_context.create_context(name if name.parent.type == 'file_input' else name.parent), module_context.create_context(name if name.parent.type == 'file_input' else name.parent),
name name
) )
) for name in get_module_names(script._get_module_node(), all_scopes) ) for name in get_module_names(script._module_node, all_scopes)
] ]
return sorted(filter(def_ref_filter, defs), key=lambda x: (x.line, x.column)) return sorted(filter(def_ref_filter, defs), key=lambda x: (x.line, x.column))

View File

@@ -555,7 +555,7 @@ class Definition(BaseDefinition):
.. todo:: Add full path. This function is should return a .. todo:: Add full path. This function is should return a
`module.class.function` path. `module.class.function` path.
""" """
position = '' if self.in_builtin_module else '@%s' % (self.line) position = '' if self.in_builtin_module else '@%s' % self.line
return "%s:%s%s" % (self.module_name, self.description, position) return "%s:%s%s" % (self.module_name, self.description, position)
@memoize_method @memoize_method

View File

@@ -64,6 +64,7 @@ that are not used are just being ignored.
from parso.python import tree from parso.python import tree
import parso import parso
from parso import python_bytes_to_unicode
from jedi import debug from jedi import debug
from jedi import parser_utils from jedi import parser_utils
@@ -106,6 +107,7 @@ class Evaluator(object):
self.access_cache = {} self.access_cache = {}
self.reset_recursion_limitations() self.reset_recursion_limitations()
self.allow_different_encoding = True
@property @property
@evaluator_function_cache() @evaluator_function_cache()
@@ -366,3 +368,15 @@ class Evaluator(object):
node = node.parent node = node.parent
scope_node = parent_scope(node) scope_node = parent_scope(node)
return from_scope_node(scope_node, is_nested=True, node_is_object=node_is_object) return from_scope_node(scope_node, is_nested=True, node_is_object=node_is_object)
def parse_and_get_code(self, code, path, **kwargs):
if self.allow_different_encoding:
if code is None:
with open(path, 'rb') as f:
code = f.read()
code = python_bytes_to_unicode(code, errors='replace')
return self.grammar.parse(code=code, path=path, **kwargs), code
def parse(self, *args, **kwargs):
return self.parse_and_get_code(*args, **kwargs)[0]

View File

@@ -10,6 +10,7 @@ from jedi.evaluate.base_context import NO_CONTEXTS
from jedi.evaluate.context import iterable from jedi.evaluate.context import iterable
from jedi.evaluate.param import get_params, ExecutedParam from jedi.evaluate.param import get_params, ExecutedParam
def try_iter_content(types, depth=0): def try_iter_content(types, depth=0):
"""Helper method for static analysis.""" """Helper method for static analysis."""
if depth > 10: if depth > 10:
@@ -29,6 +30,8 @@ def try_iter_content(types, depth=0):
class AbstractArguments(object): class AbstractArguments(object):
context = None context = None
argument_node = None
trailer = None
def eval_argument_clinic(self, parameters): def eval_argument_clinic(self, parameters):
"""Uses a list with argument clinic information (see PEP 436).""" """Uses a list with argument clinic information (see PEP 436)."""
@@ -95,10 +98,9 @@ class TreeArguments(AbstractArguments):
self.trailer = trailer # Can be None, e.g. in a class definition. self.trailer = trailer # Can be None, e.g. in a class definition.
def _split(self): def _split(self):
if isinstance(self.argument_node, (tuple, list)): if self.argument_node is None:
for el in self.argument_node: return
yield 0, el
else:
if not (self.argument_node.type == 'arglist' or ( if not (self.argument_node.type == 'arglist' or (
# in python 3.5 **arg is an argument, not arglist # in python 3.5 **arg is an argument, not arglist
(self.argument_node.type == 'argument') and (self.argument_node.type == 'argument') and
@@ -197,7 +199,11 @@ class TreeArguments(AbstractArguments):
arguments = param.var_args arguments = param.var_args
break break
return [arguments.argument_node or arguments.trailer] if arguments.argument_node is not None:
return [arguments.argument_node]
if arguments.trailer is not None:
return [arguments.trailer]
return []
class ValuesArguments(AbstractArguments): class ValuesArguments(AbstractArguments):

View File

@@ -89,7 +89,7 @@ else:
return getattr(klass, '__dict__', _sentinel) return getattr(klass, '__dict__', _sentinel)
return _shadowed_dict_newstyle(klass) return _shadowed_dict_newstyle(klass)
class _OldStyleClass(): class _OldStyleClass:
pass pass
_oldstyle_instance_type = type(_OldStyleClass()) _oldstyle_instance_type = type(_OldStyleClass())
@@ -124,7 +124,7 @@ def _safe_hasattr(obj, name):
def _safe_is_data_descriptor(obj): def _safe_is_data_descriptor(obj):
return (_safe_hasattr(obj, '__set__') or _safe_hasattr(obj, '__delete__')) return _safe_hasattr(obj, '__set__') or _safe_hasattr(obj, '__delete__')
def getattr_static(obj, attr, default=_sentinel): def getattr_static(obj, attr, default=_sentinel):

View File

@@ -66,7 +66,7 @@ class MixedName(compiled.CompiledName):
contexts = list(self.infer()) contexts = list(self.infer())
if not contexts: if not contexts:
# This means a start_pos that doesn't exist (compiled objects). # This means a start_pos that doesn't exist (compiled objects).
return (0, 0) return 0, 0
return contexts[0].name.start_pos return contexts[0].name.start_pos
@start_pos.setter @start_pos.setter

View File

@@ -132,7 +132,7 @@ class ModuleContext(use_metaclass(CachedMetaClass, TreeContext)):
def py__package__(self): def py__package__(self):
if self._get_init_directory() is None: if self._get_init_directory() is None:
return re.sub(r'\.?[^\.]+$', '', self.py__name__()) return re.sub(r'\.?[^.]+$', '', self.py__name__())
else: else:
return self.py__name__() return self.py__name__()

View File

@@ -188,7 +188,7 @@ def _evaluate_for_statement_string(module_context, string):
Need this docstring so that if the below part is not valid Python this Need this docstring so that if the below part is not valid Python this
is still a function. is still a function.
''' '''
{0} {}
""")) """))
if string is None: if string is None:
return [] return []
@@ -252,7 +252,7 @@ def _execute_array_values(evaluator, array):
for typ in lazy_context.infer() for typ in lazy_context.infer()
) )
values.append(LazyKnownContexts(objects)) values.append(LazyKnownContexts(objects))
return set([FakeSequence(evaluator, array.array_type, values)]) return {FakeSequence(evaluator, array.array_type, values)}
else: else:
return array.execute_evaluated() return array.execute_evaluated()

View File

@@ -158,7 +158,7 @@ def _check_name_for_execution(evaluator, context, compare_node, name, trailer):
def create_func_excs(): def create_func_excs():
arglist = trailer.children[1] arglist = trailer.children[1]
if arglist == ')': if arglist == ')':
arglist = () arglist = None
args = TreeArguments(evaluator, context, arglist, trailer) args = TreeArguments(evaluator, context, arglist, trailer)
if value_node.type == 'funcdef': if value_node.type == 'funcdef':
yield value.get_function_execution(args) yield value.get_function_execution(args)

View File

@@ -26,7 +26,7 @@ class AbstractNameDefinition(object):
def goto(self): def goto(self):
# Typically names are already definitions and therefore a goto on that # Typically names are already definitions and therefore a goto on that
# name will always result on itself. # name will always result on itself.
return set([self]) return {self}
def get_root_context(self): def get_root_context(self):
return self.parent_context.get_root_context() return self.parent_context.get_root_context()
@@ -386,7 +386,7 @@ def get_global_filters(evaluator, context, until_position, origin_scope):
... def func(): ... def func():
... y = None ... y = None
... ''')) ... '''))
>>> module_node = script._get_module_node() >>> module_node = script._module_node
>>> scope = next(module_node.iter_funcdefs()) >>> scope = next(module_node.iter_funcdefs())
>>> scope >>> scope
<Function: func@3-5> <Function: func@3-5>

View File

@@ -465,7 +465,7 @@ def _load_module(evaluator, path=None, code=None, sys_path=None, parent_module=N
if path is not None and path.endswith(('.py', '.zip', '.egg')) \ if path is not None and path.endswith(('.py', '.zip', '.egg')) \
and dotted_path not in settings.auto_import_modules: and dotted_path not in settings.auto_import_modules:
module_node = evaluator.grammar.parse( module_node = evaluator.parse(
code=code, path=path, cache=True, diff_cache=True, code=code, path=path, cache=True, diff_cache=True,
cache_path=settings.cache_directory) cache_path=settings.cache_directory)

View File

@@ -263,8 +263,6 @@ def collections_namedtuple(evaluator, obj, arguments):
This has to be done by processing the namedtuple class template and This has to be done by processing the namedtuple class template and
evaluating the result. evaluating the result.
.. note:: |jedi| only supports namedtuples on Python >2.6.
""" """
collections_context = obj.parent_context collections_context = obj.parent_context
_class_template_set = collections_context.py__getattribute__(u'_class_template') _class_template_set = collections_context.py__getattribute__(u'_class_template')

View File

@@ -120,7 +120,7 @@ def eval_node(context, element):
def eval_trailer(context, base_contexts, trailer): def eval_trailer(context, base_contexts, trailer):
trailer_op, node = trailer.children[:2] trailer_op, node = trailer.children[:2]
if node == ')': # `arglist` is optional. if node == ')': # `arglist` is optional.
node = () node = None
if trailer_op == '[': if trailer_op == '[':
trailer_op, node, _ = trailer.children trailer_op, node, _ = trailer.children
@@ -149,7 +149,7 @@ def eval_trailer(context, base_contexts, trailer):
name_or_str=node name_or_str=node
) )
else: else:
assert trailer_op == '(' assert trailer_op == '(', 'trailer_op is actually %s' % trailer_op
args = arguments.TreeArguments(context.evaluator, context, node, trailer) args = arguments.TreeArguments(context.evaluator, context, node, trailer)
return base_contexts.execute(args) return base_contexts.execute(args)
@@ -287,10 +287,10 @@ def eval_or_test(context, or_test):
# handle lazy evaluation of and/or here. # handle lazy evaluation of and/or here.
if operator in ('and', 'or'): if operator in ('and', 'or'):
left_bools = set(left.py__bool__() for left in types) left_bools = set(left.py__bool__() for left in types)
if left_bools == set([True]): if left_bools == {True}:
if operator == 'and': if operator == 'and':
types = context.eval_node(right) types = context.eval_node(right)
elif left_bools == set([False]): elif left_bools == {False}:
if operator != 'and': if operator != 'and':
types = context.eval_node(right) types = context.eval_node(right)
# Otherwise continue, because of uncertainty. # Otherwise continue, because of uncertainty.

View File

@@ -146,7 +146,7 @@ def detect_additional_paths(evaluator, script_path):
def _get_paths_from_buildout_script(evaluator, buildout_script_path): def _get_paths_from_buildout_script(evaluator, buildout_script_path):
try: try:
module_node = evaluator.grammar.parse( module_node = evaluator.parse(
path=buildout_script_path, path=buildout_script_path,
cache=True, cache=True,
cache_path=settings.cache_directory cache_path=settings.cache_directory

View File

@@ -4,11 +4,10 @@ from inspect import cleandoc
from jedi._compatibility import literal_eval, force_unicode from jedi._compatibility import literal_eval, force_unicode
from parso.python import tree from parso.python import tree
_EXECUTE_NODES = set([ _EXECUTE_NODES = {'funcdef', 'classdef', 'import_from', 'import_name', 'test',
'funcdef', 'classdef', 'import_from', 'import_name', 'test', 'or_test', 'or_test', 'and_test', 'not_test', 'comparison', 'expr',
'and_test', 'not_test', 'comparison', 'expr', 'xor_expr', 'and_expr', 'xor_expr', 'and_expr', 'shift_expr', 'arith_expr',
'shift_expr', 'arith_expr', 'atom_expr', 'term', 'factor', 'power', 'atom' 'atom_expr', 'term', 'factor', 'power', 'atom'}
])
_FLOW_KEYWORDS = ( _FLOW_KEYWORDS = (
'try', 'except', 'finally', 'else', 'if', 'elif', 'with', 'for', 'while' 'try', 'except', 'finally', 'else', 'if', 'elif', 'with', 'for', 'while'

View File

@@ -89,7 +89,7 @@ def setup_readline(namespace_module=__main__):
lines = split_lines(text) lines = split_lines(text)
position = (len(lines), len(lines[-1])) position = (len(lines), len(lines[-1]))
name = get_on_completion_name( name = get_on_completion_name(
interpreter._get_module_node(), interpreter._module_node,
lines, lines,
position position
) )

View File

@@ -32,7 +32,8 @@ setup(name='jedi',
license='MIT', license='MIT',
keywords='python completion refactoring vim', keywords='python completion refactoring vim',
long_description=readme, long_description=readme,
packages=find_packages(exclude=['test']), packages=find_packages(exclude=['test', 'test.*']),
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*',
install_requires=install_requires, install_requires=install_requires,
extras_require={'dev': ['docopt']}, extras_require={'dev': ['docopt']},
package_data={'jedi': ['evaluate/compiled/fake/*.pym']}, package_data={'jedi': ['evaluate/compiled/fake/*.pym']},
@@ -44,7 +45,6 @@ setup(name='jedi',
'License :: OSI Approved :: MIT License', 'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent', 'Operating System :: OS Independent',
'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.3',

View File

@@ -403,8 +403,6 @@ def test_func():
x x
# python >= 2.7
# Set literals are not valid in 2.6.
#? int() #? int()
tuple({1})[0] tuple({1})[0]

View File

@@ -286,8 +286,6 @@ with open('') as f:
#? str() #? str()
line line
# Nested with statements don't exist in Python 2.6.
# python >= 2.7
with open('') as f1, open('') as f2: with open('') as f1, open('') as f2:
#? ['closed'] #? ['closed']
f1.closed f1.closed

View File

@@ -210,6 +210,5 @@ d[2]
next(iter({a for a in range(10)})) next(iter({a for a in range(10)}))
# with a set literal (also doesn't work in 2.6).
#? int() #? int()
[a for a in {1, 2, 3}][0] [a for a in {1, 2, 3}][0]

View File

@@ -211,6 +211,17 @@ class X():
#? #?
self.x() self.x()
def decorator_var_args(function, *args):
return function(*args)
@decorator_var_args
def function_var_args(param):
return param
#? int()
function_var_args(1)
# ----------------- # -----------------
# method decorators # method decorators
# ----------------- # -----------------

View File

@@ -288,8 +288,6 @@ third()[0]
# ----------------- # -----------------
# set.add # set.add
# ----------------- # -----------------
# Set literals are not valid in 2.6.
# python >= 2.7
st = {1.0} st = {1.0}
for a in [1,2]: for a in [1,2]:
st.add(a) st.add(a)

View File

@@ -47,10 +47,6 @@ b
class Employee: class Employee:
pass pass
# The typing library is not installable for Python 2.6, therefore ignore the
# following tests.
# python >= 2.7
from typing import List from typing import List
x = [] # type: List[Employee] x = [] # type: List[Employee]
#? Employee() #? Employee()

View File

@@ -3,8 +3,6 @@ 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 are not supported in python 2.7 else then annotating by comment (and this is
still TODO at 2016-01-23) still TODO at 2016-01-23)
""" """
# There's no Python 2.6 typing module.
# python >= 2.7
import typing import typing
class B: class B:
pass pass

View File

@@ -116,8 +116,6 @@ tup4.index
# ----------------- # -----------------
# set # set
# ----------------- # -----------------
# Set literals are not valid in 2.6.
# python >= 2.7
set_t = {1,2} set_t = {1,2}
#? ['clear', 'copy'] #? ['clear', 'copy']

View File

@@ -296,8 +296,6 @@ x = 32
[x for x in something] [x for x in something]
x = 3 x = 3
# Not supported syntax in Python 2.6.
# python >= 2.7
#< 1 (0,1), (0,10) #< 1 (0,1), (0,10)
{x:1 for x in something} {x:1 for x in something}
#< 10 (0,1), (0,10) #< 10 (0,1), (0,10)

View File

@@ -41,9 +41,9 @@ def parse_test_files_option(opt):
opt = str(opt) opt = str(opt)
if ':' in opt: if ':' in opt:
(f_name, rest) = opt.split(':', 1) (f_name, rest) = opt.split(':', 1)
return (f_name, list(map(int, rest.split(',')))) return f_name, list(map(int, rest.split(',')))
else: else:
return (opt, []) return opt, []
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
@@ -127,7 +127,7 @@ class StaticAnalysisCase(object):
@pytest.fixture() @pytest.fixture()
def cwd_tmpdir(monkeypatch, tmpdir): def cwd_tmpdir(monkeypatch, tmpdir):
with helpers.set_cwd(tmpdir.dirpath): with helpers.set_cwd(tmpdir.strpath):
yield tmpdir yield tmpdir

View File

@@ -206,7 +206,7 @@ class IntegrationTestCase(object):
completions = self.script(environment).completions() completions = self.script(environment).completions()
#import cProfile; cProfile.run('script.completions()') #import cProfile; cProfile.run('script.completions()')
comp_str = set([c.name for c in completions]) comp_str = {c.name for c in completions}
return compare_cb(self, comp_str, set(literal_eval(self.correct))) return compare_cb(self, comp_str, set(literal_eval(self.correct)))
def run_goto_definitions(self, compare_cb, environment): def run_goto_definitions(self, compare_cb, environment):

View File

@@ -103,8 +103,8 @@ def test_completion_on_complex_literals(Script):
_check_number(Script, '4.0j.', 'complex') _check_number(Script, '4.0j.', 'complex')
# No dot no completion - I thought, but 4j is actually a literall after # No dot no completion - I thought, but 4j is actually a literall after
# which a keyword like or is allowed. Good times, haha! # which a keyword like or is allowed. Good times, haha!
assert (set([c.name for c in Script('4j').completions()]) == assert ({c.name for c in Script('4j').completions()} ==
set(['if', 'and', 'in', 'is', 'not', 'or'])) {'if', 'and', 'in', 'is', 'not', 'or'})
def test_goto_assignments_on_non_name(Script, environment): def test_goto_assignments_on_non_name(Script, environment):
@@ -152,7 +152,7 @@ def test_goto_definition_not_multiple(Script):
def test_usage_description(Script): def test_usage_description(Script):
descs = [u.description for u in Script("foo = ''; foo").usages()] descs = [u.description for u in Script("foo = ''; foo").usages()]
assert set(descs) == set(["foo = ''", 'foo']) assert set(descs) == {"foo = ''", 'foo'}
def test_get_line_code(Script): def test_get_line_code(Script):

View File

@@ -34,7 +34,7 @@ def test_follow_import_incomplete(Script):
# incomplete `from * import` part # incomplete `from * import` part
datetime = check_follow_definition_types(Script, "from datetime import datetim") datetime = check_follow_definition_types(Script, "from datetime import datetim")
assert set(datetime) == set(['class', 'instance']) # py33: builtin and pure py version assert set(datetime) == {'class', 'instance'} # py33: builtin and pure py version
# os.path check # os.path check
ospath = check_follow_definition_types(Script, "from os.path import abspat") ospath = check_follow_definition_types(Script, "from os.path import abspat")

View File

@@ -68,25 +68,25 @@ def test_basedefinition_type(Script, environment):
def test_basedefinition_type_import(Script): def test_basedefinition_type_import(Script):
def get_types(source, **kwargs): def get_types(source, **kwargs):
return set([t.type for t in Script(source, **kwargs).completions()]) return {t.type for t in Script(source, **kwargs).completions()}
# import one level # import one level
assert get_types('import t') == set(['module']) assert get_types('import t') == {'module'}
assert get_types('import ') == set(['module']) assert get_types('import ') == {'module'}
assert get_types('import datetime; datetime') == set(['module']) assert get_types('import datetime; datetime') == {'module'}
# from # from
assert get_types('from datetime import timedelta') == set(['class']) assert get_types('from datetime import timedelta') == {'class'}
assert get_types('from datetime import timedelta; timedelta') == set(['class']) assert get_types('from datetime import timedelta; timedelta') == {'class'}
assert get_types('from json import tool') == set(['module']) assert get_types('from json import tool') == {'module'}
assert get_types('from json import tool; tool') == set(['module']) assert get_types('from json import tool; tool') == {'module'}
# import two levels # import two levels
assert get_types('import json.tool; json') == set(['module']) assert get_types('import json.tool; json') == {'module'}
assert get_types('import json.tool; json.tool') == set(['module']) assert get_types('import json.tool; json.tool') == {'module'}
assert get_types('import json.tool; json.tool.main') == set(['function']) assert get_types('import json.tool; json.tool.main') == {'function'}
assert get_types('import json.tool') == set(['module']) assert get_types('import json.tool') == {'module'}
assert get_types('import json.tool', column=9) == set(['module']) assert get_types('import json.tool', column=9) == {'module'}
def test_function_call_signature_in_doc(Script): def test_function_call_signature_in_doc(Script):

View File

@@ -16,8 +16,8 @@ else:
exec source in global_map """, 'blub', 'exec')) exec source in global_map """, 'blub', 'exec'))
class _GlobalNameSpace(): class _GlobalNameSpace:
class SideEffectContainer(): class SideEffectContainer:
pass pass
@@ -80,7 +80,7 @@ def test_numpy_like_non_zero():
def test_nested_resolve(): def test_nested_resolve():
class XX(): class XX:
def x(): def x():
pass pass
@@ -168,7 +168,7 @@ def test_list():
def test_slice(): def test_slice():
class Foo1(): class Foo1:
bar = [] bar = []
baz = 'xbarx' baz = 'xbarx'
_assert_interpreter_complete('getattr(Foo1, baz[1:-1]).append', _assert_interpreter_complete('getattr(Foo1, baz[1:-1]).append',
@@ -177,7 +177,7 @@ def test_slice():
def test_getitem_side_effects(): def test_getitem_side_effects():
class Foo2(): class Foo2:
def __getitem__(self, index): def __getitem__(self, index):
# Possible side effects here, should therefore not call this. # Possible side effects here, should therefore not call this.
if True: if True:
@@ -190,7 +190,7 @@ def test_getitem_side_effects():
def test_property_error_oldstyle(): def test_property_error_oldstyle():
lst = [] lst = []
class Foo3(): class Foo3:
@property @property
def bar(self): def bar(self):
lst.append(1) lst.append(1)
@@ -261,7 +261,7 @@ def test_completion_param_annotations():
a, b, c = c.params a, b, c = c.params
assert a._goto_definitions() == [] assert a._goto_definitions() == []
assert [d.name for d in b._goto_definitions()] == ['str'] assert [d.name for d in b._goto_definitions()] == ['str']
assert set([d.name for d in c._goto_definitions()]) == set(['int', 'float']) assert {d.name for d in c._goto_definitions()} == {'int', 'float'}
def test_more_complex_instances(): def test_more_complex_instances():
@@ -269,7 +269,7 @@ def test_more_complex_instances():
def foo(self, other): def foo(self, other):
return self return self
class Base(): class Base:
def wow(self): def wow(self):
return Something() return Something()

View File

@@ -63,3 +63,12 @@ def test_complete_at_zero(Script):
s = Script("", 1, 0).completions() s = Script("", 1, 0).completions()
assert len(s) > 0 assert len(s) > 0
def test_wrong_encoding(Script, cwd_tmpdir):
x = cwd_tmpdir.join('x.py')
# Use both latin-1 and utf-8 (a really broken file).
x.write_binary(u'foobar = 1\nä'.encode('latin-1') + 'ä'.encode())
c, = Script('import x; x.foo', sys_path=['.']).completions()
assert c.name == 'foobar'

View File

@@ -1,7 +1,7 @@
def test_module_attributes(Script): def test_module_attributes(Script):
def_, = Script('__name__').completions() def_, = Script('__name__').completions()
assert def_.name == '__name__' assert def_.name == '__name__'
assert def_.line == None assert def_.line is None
assert def_.column == None assert def_.column is None
str_, = def_._goto_definitions() str_, = def_._goto_definitions()
assert str_.name == 'str' assert str_.name == 'str'

View File

@@ -10,8 +10,6 @@ import pytest
from jedi._compatibility import find_module_py33, find_module from jedi._compatibility import find_module_py33, find_module
from ..helpers import cwd_at from ..helpers import cwd_at
from jedi._compatibility import is_py26
@pytest.mark.skipif('sys.version_info < (3,3)') @pytest.mark.skipif('sys.version_info < (3,3)')
def test_find_module_py33(): def test_find_module_py33():
@@ -163,9 +161,8 @@ def test_complete_on_empty_import(Script):
# relative import # relative import
assert 10 < len(Script("from . import classes", 1, 6, 'whatever.py').completions()) < 30 assert 10 < len(Script("from . import classes", 1, 6, 'whatever.py').completions()) < 30
wanted = set(['ImportError', 'import', 'ImportWarning']) wanted = {'ImportError', 'import', 'ImportWarning'}
assert set([c.name for c in Script("import").completions()]) == wanted assert {c.name for c in Script("import").completions()} == wanted
if not is_py26: # python 2.6 doesn't always come with a library `import*`.
assert len(Script("import import", path='').completions()) > 0 assert len(Script("import import", path='').completions()) > 0
# 111 # 111

View File

@@ -5,7 +5,6 @@ with "Black Box Tests".
from textwrap import dedent from textwrap import dedent
import pytest import pytest
from jedi._compatibility import is_py26
# The namedtuple is different for different Python2.7 versions. Some versions # The namedtuple is different for different Python2.7 versions. Some versions
@@ -28,9 +27,6 @@ def test_namedtuple_str(letter, expected, Script):
dave.%s""") % letter dave.%s""") % letter
result = Script(source).completions() result = Script(source).completions()
completions = set(r.name for r in result) completions = set(r.name for r in result)
if is_py26:
assert completions == set()
else:
assert completions == set(expected) assert completions == set(expected)
@@ -42,10 +38,7 @@ def test_namedtuple_list(Script):
garfield.l""") garfield.l""")
result = Script(source).completions() result = Script(source).completions()
completions = set(r.name for r in result) completions = set(r.name for r in result)
if is_py26: assert completions == {'legs', 'length', 'large'}
assert completions == set()
else:
assert completions == set(['legs', 'length', 'large'])
def test_namedtuple_content(Script): def test_namedtuple_content(Script):

View File

@@ -15,9 +15,9 @@ def test_paths_from_assignment(Script):
expr_stmt = script._get_module_node().children[0] expr_stmt = script._get_module_node().children[0]
return set(sys_path._paths_from_assignment(script._get_module(), expr_stmt)) return set(sys_path._paths_from_assignment(script._get_module(), expr_stmt))
assert paths('sys.path[0:0] = ["a"]') == set(['/foo/a']) assert paths('sys.path[0:0] = ["a"]') == {'/foo/a'}
assert paths('sys.path = ["b", 1, x + 3, y, "c"]') == set(['/foo/b', '/foo/c']) assert paths('sys.path = ["b", 1, x + 3, y, "c"]') == {'/foo/b', '/foo/c'}
assert paths('sys.path = a = ["a"]') == set(['/foo/a']) assert paths('sys.path = a = ["a"]') == {'/foo/a'}
# Fail for complicated examples. # Fail for complicated examples.
assert paths('sys.path, other = ["a"], 2') == set() assert paths('sys.path, other = ["a"], 2') == set()

View File

@@ -7,7 +7,7 @@ from parso.python import tree
import pytest import pytest
class TestCallAndName(): class TestCallAndName:
def get_call(self, source): def get_call(self, source):
# Get the simple_stmt and then the first one. # Get the simple_stmt and then the first one.
node = parse(source).children[0] node = parse(source).children[0]

View File

@@ -41,7 +41,6 @@ def test_scipy_speed(Script):
s = 'import scipy.weave; scipy.weave.inline(' s = 'import scipy.weave; scipy.weave.inline('
script = Script(s, 1, len(s), '') script = Script(s, 1, len(s), '')
script.call_signatures() script.call_signatures()
#print(jedi.imports.imports_processed)
@_check_speed(0.8) @_check_speed(0.8)
@@ -63,10 +62,11 @@ def test_no_repr_computation(Script):
unwanted computation of repr(). Exemple : big pandas data. unwanted computation of repr(). Exemple : big pandas data.
See issue #919. See issue #919.
""" """
class SlowRepr(): class SlowRepr:
"class to test what happens if __repr__ is very slow." "class to test what happens if __repr__ is very slow."
def some_method(self): def some_method(self):
pass pass
def __repr__(self): def __repr__(self):
time.sleep(0.2) time.sleep(0.2)
test = SlowRepr() test = SlowRepr()

View File

@@ -56,7 +56,7 @@ class TestSetupReadline(unittest.TestCase):
string = 'os.path.join("a").upper' string = 'os.path.join("a").upper'
assert self.completions(string) == [string] assert self.completions(string) == [string]
c = set(['os.' + d for d in dir(os) if d.startswith('ch')]) c = {'os.' + d for d in dir(os) if d.startswith('ch')}
assert set(self.completions('os.ch')) == set(c) assert set(self.completions('os.ch')) == set(c)
finally: finally:
del self.namespace.sys del self.namespace.sys
@@ -68,12 +68,12 @@ class TestSetupReadline(unittest.TestCase):
def test_import(self): def test_import(self):
s = 'from os.path import a' s = 'from os.path import a'
assert set(self.completions(s)) == set([s + 'ltsep', s + 'bspath']) assert set(self.completions(s)) == {s + 'ltsep', s + 'bspath'}
assert self.completions('import keyword') == ['import keyword'] assert self.completions('import keyword') == ['import keyword']
import os import os
s = 'from os import ' s = 'from os import '
goal = set([s + el for el in dir(os)]) goal = {s + el for el in dir(os)}
# There are minor differences, e.g. the dir doesn't include deleted # There are minor differences, e.g. the dir doesn't include deleted
# items as well as items that are not only available on linux. # items as well as items that are not only available on linux.
assert len(set(self.completions(s)).symmetric_difference(goal)) < 20 assert len(set(self.completions(s)).symmetric_difference(goal)) < 20
@@ -85,7 +85,7 @@ class TestSetupReadline(unittest.TestCase):
def test_preexisting_values(self): def test_preexisting_values(self):
self.namespace.a = range(10) self.namespace.a = range(10)
assert set(self.completions('a.')) == set(['a.' + n for n in dir(range(1))]) assert set(self.completions('a.')) == {'a.' + n for n in dir(range(1))}
del self.namespace.a del self.namespace.a
def test_colorama(self): def test_colorama(self):

View File

@@ -24,10 +24,6 @@ setenv =
env37: JEDI_TEST_ENVIRONMENT=37 env37: JEDI_TEST_ENVIRONMENT=37
commands = commands =
py.test {posargs:jedi test} py.test {posargs:jedi test}
[testenv:py26]
deps =
unittest2
{[testenv]deps}
[testenv:py27] [testenv:py27]
deps = deps =
# for testing the typing module # for testing the typing module