diff --git a/.gitignore b/.gitignore index 369f71c6..ec2920f1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .tox .coveralls.yml .coverage +.idea /build/ /docs/_build/ /dist/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d9873229..05f454f5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,12 @@ Changelog --------- +0.12.0 (2018-01-01) ++++++++++++++++++++ + +- Remove Python 2.6 +- VirtualEnvs + 0.11.1 (2017-12-14) +++++++++++++++++++ diff --git a/README.rst b/README.rst index d698bc7c..e8c8d7d5 100644 --- a/README.rst +++ b/README.rst @@ -2,16 +2,23 @@ Jedi - an awesome autocompletion/static analysis library for Python ################################################################### -.. image:: https://secure.travis-ci.org/davidhalter/jedi.png?branch=master - :target: http://travis-ci.org/davidhalter/jedi - :alt: Travis-CI build status +.. image:: https://img.shields.io/pypi/v/jedi.svg?style=flat + :target: https://pypi.python.org/pypi/jedi + :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 - :alt: Coverage Status + :alt: Coverage status - -*If you have specific questions, please add an issue or ask on* `stackoverflow +*If you have specific questions, please add an issue or ask on* `Stack Overflow `_ *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 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 `_, which uses Jedi's autocompletion. We encourage you to use Jedi in your IDEs. It's really easy. @@ -45,7 +52,7 @@ Jedi can currently be used with the following editors/projects: - Gedit (gedi_) - wdb_ - Web Debugger - `Eric IDE`_ (Available as a plugin) -- `Ipython 6.0.0+ `_ +- `IPython 6.0.0+ `_ and many more! @@ -96,7 +103,7 @@ understands, see: `Features `_. A list of 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. 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.) ------------------------------------------- -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. It's possible to have Jedi autocompletion in REPL modes - `example video `_. @@ -178,7 +185,7 @@ Tests are also run automatically on `Travis CI `_. For more detailed information visit the `testing documentation -`_ +`_. Acknowledgements diff --git a/docs/_themes/flask/layout.html b/docs/_themes/flask/layout.html index e781a119..48cb4d5f 100644 --- a/docs/_themes/flask/layout.html +++ b/docs/_themes/flask/layout.html @@ -19,7 +19,6 @@ {% endblock %} {%- block footer %} {% if pagename == 'index' %} diff --git a/docs/conf.py b/docs/conf.py index 37ae0642..4199bca8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -45,7 +45,7 @@ master_doc = 'index' # General information about the project. project = u'Jedi' -copyright = u'2012 - {today.year}, Jedi contributors'.format(today=datetime.date.today()) +copyright = u'jedi contributors' import jedi from jedi.utils import version_info diff --git a/docs/docs/features.rst b/docs/docs/features.rst index ad4e900c..c8f478ba 100644 --- a/docs/docs/features.rst +++ b/docs/docs/features.rst @@ -20,7 +20,7 @@ make it work. General Features ---------------- -- python 2.6+ and 3.3+ support +- Python 2.7 and 3.3+ support - ignores syntax errors and wrong indentation - can deal with complex module / function / class structures - virtualenv support @@ -64,7 +64,7 @@ Not yet implemented: - manipulations of instances outside the instance variables without using methods -- implicit namespace packages (Python 3.3+, `PEP 420 `_) +- implicit namespace packages (Python 3.4+, `PEP 420 `_) Will probably never be implemented: @@ -88,7 +88,7 @@ etc. **Security** 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 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 @@ -117,7 +117,7 @@ one of the following docstring/annotation syntax styles: 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) :: @@ -129,7 +129,7 @@ comments in planned but not yet implemented) 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 :: @@ -142,7 +142,7 @@ Note that the type hints must be on the same line as the statement print(f + 3) 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. Things that are missing (and this is not an exhaustive list; some of these diff --git a/jedi/_compatibility.py b/jedi/_compatibility.py index e6eabe43..e3e50e5f 100644 --- a/jedi/_compatibility.py +++ b/jedi/_compatibility.py @@ -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. """ import sys @@ -13,12 +13,10 @@ try: except ImportError: 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_py33 = is_py3 and sys.version_info[1] >= 3 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])) @@ -74,7 +72,7 @@ def find_module_py33(string, path=None, loader=None, full_name=None): raise ImportError("Originally " + repr(e)) 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: is_package = loader.is_package(string) @@ -127,14 +125,7 @@ def find_module_pre_py33(string, path=None, full_name=None): if loader: is_package = loader.is_package(string) is_archive = hasattr(loader, 'archive') - try: - module_path = loader.get_filename(string) - except AttributeError: - # fallback for py26 - try: - module_path = loader._get_filename(string) - except AttributeError: - continue + module_path = loader.get_filename(string) if is_package: module_path = os.path.dirname(module_path) if is_archive: @@ -142,10 +133,10 @@ def find_module_pre_py33(string, path=None, full_name=None): file = None if not is_package or is_archive: file = DummyFile(loader, string) - return (file, module_path, is_package) + return file, module_path, is_package except ImportError: 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 @@ -260,11 +251,6 @@ import ast 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) diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index 18b98959..49d0859b 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -12,8 +12,8 @@ arguments. import os import sys +import parso from parso.python import tree -from parso import python_bytes_to_unicode, split_lines from jedi._compatibility import force_unicode, is_py3 from jedi.parser_utils import get_executable_nodes @@ -94,9 +94,29 @@ class Script(object): with open(path, 'rb') as f: source = f.read() - # TODO do we really want that? - self._source = python_bytes_to_unicode(source, encoding, errors='replace') - self._code_lines = split_lines(self._source) + # Load the Python grammar of the current interpreter. + self._grammar = parso.load_grammar() + + 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 if not (0 < line <= len(self._code_lines)): raise ValueError('`line` parameter is not in a valid range.') @@ -111,35 +131,8 @@ class Script(object): cache.clear_time_caches() 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): - module = ModuleContext( - self._evaluator, - self._get_module_node(), - self.path - ) + module = ModuleContext(self._evaluator, self._module_node, self.path) if self.path is not None: name = dotted_path_in_sys_path(self._evaluator.get_sys_path(), self.path) if name is not None: @@ -178,10 +171,9 @@ class Script(object): :rtype: list of :class:`classes.Definition` """ - module_node = self._get_module_node() - leaf = module_node.get_name_of_position(self._pos) + leaf = self._module_node.get_name_of_position(self._pos) 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: return [] @@ -212,7 +204,7 @@ class Script(object): else: 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: return [] context = self._evaluator.create_context(self._get_module(), tree_name) @@ -243,7 +235,7 @@ class Script(object): :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: # Must be syntax return [] @@ -270,7 +262,7 @@ class Script(object): :rtype: list of :class:`classes.CallSignature` """ 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: return [] @@ -295,10 +287,9 @@ class Script(object): def _analysis(self): self._evaluator.is_analysis = True - module_node = self._get_module_node() - self._evaluator.analysis_modules = [module_node] + self._evaluator.analysis_modules = [self._module_node] 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) if node.type in ('funcdef', 'classdef'): # Resolve the decorators. @@ -374,10 +365,9 @@ class Interpreter(Script): self.namespaces = namespaces def _get_module(self): - parser_module = super(Interpreter, self)._get_module_node() return interpreter.MixedModuleContext( self._evaluator, - parser_module, + self._module_node, self.namespaces, 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), 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)) diff --git a/jedi/api/classes.py b/jedi/api/classes.py index a2f5ffa3..1952f045 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -555,7 +555,7 @@ class Definition(BaseDefinition): .. todo:: Add full path. This function is should return a `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) @memoize_method diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 2c1bb397..c5b586f6 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -64,6 +64,7 @@ that are not used are just being ignored. from parso.python import tree import parso +from parso import python_bytes_to_unicode from jedi import debug from jedi import parser_utils @@ -106,6 +107,7 @@ class Evaluator(object): self.access_cache = {} self.reset_recursion_limitations() + self.allow_different_encoding = True @property @evaluator_function_cache() @@ -366,3 +368,15 @@ class Evaluator(object): node = node.parent scope_node = parent_scope(node) 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] diff --git a/jedi/evaluate/arguments.py b/jedi/evaluate/arguments.py index 32b9238c..48c0d5e6 100644 --- a/jedi/evaluate/arguments.py +++ b/jedi/evaluate/arguments.py @@ -10,6 +10,7 @@ from jedi.evaluate.base_context import NO_CONTEXTS from jedi.evaluate.context import iterable from jedi.evaluate.param import get_params, ExecutedParam + def try_iter_content(types, depth=0): """Helper method for static analysis.""" if depth > 10: @@ -29,6 +30,8 @@ def try_iter_content(types, depth=0): class AbstractArguments(object): context = None + argument_node = None + trailer = None def eval_argument_clinic(self, parameters): """Uses a list with argument clinic information (see PEP 436).""" @@ -95,29 +98,28 @@ class TreeArguments(AbstractArguments): self.trailer = trailer # Can be None, e.g. in a class definition. def _split(self): - if isinstance(self.argument_node, (tuple, list)): - for el in self.argument_node: - yield 0, el - else: - if not (self.argument_node.type == 'arglist' or ( - # in python 3.5 **arg is an argument, not arglist - (self.argument_node.type == 'argument') and - self.argument_node.children[0] in ('*', '**'))): - yield 0, self.argument_node - return + if self.argument_node is None: + return - iterator = iter(self.argument_node.children) - for child in iterator: - if child == ',': - continue - elif child in ('*', '**'): - yield len(child.value), next(iterator) - elif child.type == 'argument' and \ - child.children[0] in ('*', '**'): - assert len(child.children) == 2 - yield len(child.children[0].value), child.children[1] - else: - yield 0, child + if not (self.argument_node.type == 'arglist' or ( + # in python 3.5 **arg is an argument, not arglist + (self.argument_node.type == 'argument') and + self.argument_node.children[0] in ('*', '**'))): + yield 0, self.argument_node + return + + iterator = iter(self.argument_node.children) + for child in iterator: + if child == ',': + continue + elif child in ('*', '**'): + yield len(child.value), next(iterator) + elif child.type == 'argument' and \ + child.children[0] in ('*', '**'): + assert len(child.children) == 2 + yield len(child.children[0].value), child.children[1] + else: + yield 0, child def unpack(self, funcdef=None): named_args = [] @@ -197,7 +199,11 @@ class TreeArguments(AbstractArguments): arguments = param.var_args 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): diff --git a/jedi/evaluate/compiled/getattr_static.py b/jedi/evaluate/compiled/getattr_static.py index 795a35ce..946ac09b 100644 --- a/jedi/evaluate/compiled/getattr_static.py +++ b/jedi/evaluate/compiled/getattr_static.py @@ -89,7 +89,7 @@ else: return getattr(klass, '__dict__', _sentinel) return _shadowed_dict_newstyle(klass) - class _OldStyleClass(): + class _OldStyleClass: pass _oldstyle_instance_type = type(_OldStyleClass()) @@ -124,7 +124,7 @@ def _safe_hasattr(obj, name): 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): diff --git a/jedi/evaluate/compiled/mixed.py b/jedi/evaluate/compiled/mixed.py index c701108d..6278dd8a 100644 --- a/jedi/evaluate/compiled/mixed.py +++ b/jedi/evaluate/compiled/mixed.py @@ -66,7 +66,7 @@ class MixedName(compiled.CompiledName): contexts = list(self.infer()) if not contexts: # This means a start_pos that doesn't exist (compiled objects). - return (0, 0) + return 0, 0 return contexts[0].name.start_pos @start_pos.setter diff --git a/jedi/evaluate/context/module.py b/jedi/evaluate/context/module.py index e4f45259..78d80df4 100644 --- a/jedi/evaluate/context/module.py +++ b/jedi/evaluate/context/module.py @@ -132,7 +132,7 @@ class ModuleContext(use_metaclass(CachedMetaClass, TreeContext)): def py__package__(self): if self._get_init_directory() is None: - return re.sub(r'\.?[^\.]+$', '', self.py__name__()) + return re.sub(r'\.?[^.]+$', '', self.py__name__()) else: return self.py__name__() diff --git a/jedi/evaluate/docstrings.py b/jedi/evaluate/docstrings.py index 81798c3d..3865093d 100644 --- a/jedi/evaluate/docstrings.py +++ b/jedi/evaluate/docstrings.py @@ -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 is still a function. ''' - {0} + {} """)) if string is None: return [] @@ -252,7 +252,7 @@ def _execute_array_values(evaluator, array): for typ in lazy_context.infer() ) values.append(LazyKnownContexts(objects)) - return set([FakeSequence(evaluator, array.array_type, values)]) + return {FakeSequence(evaluator, array.array_type, values)} else: return array.execute_evaluated() diff --git a/jedi/evaluate/dynamic.py b/jedi/evaluate/dynamic.py index 95836bf5..d91a26a1 100644 --- a/jedi/evaluate/dynamic.py +++ b/jedi/evaluate/dynamic.py @@ -158,7 +158,7 @@ def _check_name_for_execution(evaluator, context, compare_node, name, trailer): def create_func_excs(): arglist = trailer.children[1] if arglist == ')': - arglist = () + arglist = None args = TreeArguments(evaluator, context, arglist, trailer) if value_node.type == 'funcdef': yield value.get_function_execution(args) diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index b980d46e..f34b2810 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -26,7 +26,7 @@ class AbstractNameDefinition(object): def goto(self): # Typically names are already definitions and therefore a goto on that # name will always result on itself. - return set([self]) + return {self} def get_root_context(self): return self.parent_context.get_root_context() @@ -386,7 +386,7 @@ def get_global_filters(evaluator, context, until_position, origin_scope): ... def func(): ... y = None ... ''')) - >>> module_node = script._get_module_node() + >>> module_node = script._module_node >>> scope = next(module_node.iter_funcdefs()) >>> scope diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index ba37dc44..98768a47 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -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')) \ 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, cache_path=settings.cache_directory) diff --git a/jedi/evaluate/stdlib.py b/jedi/evaluate/stdlib.py index af17ff1b..8ed60b10 100644 --- a/jedi/evaluate/stdlib.py +++ b/jedi/evaluate/stdlib.py @@ -263,8 +263,6 @@ def collections_namedtuple(evaluator, obj, arguments): This has to be done by processing the namedtuple class template and evaluating the result. - .. note:: |jedi| only supports namedtuples on Python >2.6. - """ collections_context = obj.parent_context _class_template_set = collections_context.py__getattribute__(u'_class_template') diff --git a/jedi/evaluate/syntax_tree.py b/jedi/evaluate/syntax_tree.py index bd70b2c2..98711943 100644 --- a/jedi/evaluate/syntax_tree.py +++ b/jedi/evaluate/syntax_tree.py @@ -120,7 +120,7 @@ def eval_node(context, element): def eval_trailer(context, base_contexts, trailer): trailer_op, node = trailer.children[:2] if node == ')': # `arglist` is optional. - node = () + node = None if trailer_op == '[': trailer_op, node, _ = trailer.children @@ -149,7 +149,7 @@ def eval_trailer(context, base_contexts, trailer): name_or_str=node ) else: - assert trailer_op == '(' + assert trailer_op == '(', 'trailer_op is actually %s' % trailer_op args = arguments.TreeArguments(context.evaluator, context, node, trailer) return base_contexts.execute(args) @@ -287,10 +287,10 @@ def eval_or_test(context, or_test): # 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 left_bools == {True}: if operator == 'and': types = context.eval_node(right) - elif left_bools == set([False]): + elif left_bools == {False}: if operator != 'and': types = context.eval_node(right) # Otherwise continue, because of uncertainty. diff --git a/jedi/evaluate/sys_path.py b/jedi/evaluate/sys_path.py index 4eff3b76..7dd42938 100644 --- a/jedi/evaluate/sys_path.py +++ b/jedi/evaluate/sys_path.py @@ -146,7 +146,7 @@ def detect_additional_paths(evaluator, script_path): def _get_paths_from_buildout_script(evaluator, buildout_script_path): try: - module_node = evaluator.grammar.parse( + module_node = evaluator.parse( path=buildout_script_path, cache=True, cache_path=settings.cache_directory diff --git a/jedi/parser_utils.py b/jedi/parser_utils.py index 6991dfa6..6b2d6236 100644 --- a/jedi/parser_utils.py +++ b/jedi/parser_utils.py @@ -4,11 +4,10 @@ from inspect import cleandoc from jedi._compatibility import literal_eval, force_unicode from parso.python import tree -_EXECUTE_NODES = set([ - 'funcdef', 'classdef', 'import_from', 'import_name', 'test', 'or_test', - 'and_test', 'not_test', 'comparison', 'expr', 'xor_expr', 'and_expr', - 'shift_expr', 'arith_expr', 'atom_expr', 'term', 'factor', 'power', 'atom' -]) +_EXECUTE_NODES = {'funcdef', 'classdef', 'import_from', 'import_name', 'test', + 'or_test', 'and_test', 'not_test', 'comparison', 'expr', + 'xor_expr', 'and_expr', 'shift_expr', 'arith_expr', + 'atom_expr', 'term', 'factor', 'power', 'atom'} _FLOW_KEYWORDS = ( 'try', 'except', 'finally', 'else', 'if', 'elif', 'with', 'for', 'while' diff --git a/jedi/utils.py b/jedi/utils.py index 177524c5..12b36b76 100644 --- a/jedi/utils.py +++ b/jedi/utils.py @@ -89,7 +89,7 @@ def setup_readline(namespace_module=__main__): lines = split_lines(text) position = (len(lines), len(lines[-1])) name = get_on_completion_name( - interpreter._get_module_node(), + interpreter._module_node, lines, position ) diff --git a/setup.py b/setup.py index 3f3b7e54..ea9a9794 100755 --- a/setup.py +++ b/setup.py @@ -32,7 +32,8 @@ setup(name='jedi', license='MIT', keywords='python completion refactoring vim', 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, extras_require={'dev': ['docopt']}, package_data={'jedi': ['evaluate/compiled/fake/*.pym']}, @@ -44,7 +45,6 @@ setup(name='jedi', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', diff --git a/test/completion/arrays.py b/test/completion/arrays.py index 8be1cece..e44a0671 100644 --- a/test/completion/arrays.py +++ b/test/completion/arrays.py @@ -403,8 +403,6 @@ def test_func(): x -# python >= 2.7 -# Set literals are not valid in 2.6. #? int() tuple({1})[0] diff --git a/test/completion/basic.py b/test/completion/basic.py index 34e88100..d79a15ed 100644 --- a/test/completion/basic.py +++ b/test/completion/basic.py @@ -286,8 +286,6 @@ with open('') as f: #? str() line -# Nested with statements don't exist in Python 2.6. -# python >= 2.7 with open('') as f1, open('') as f2: #? ['closed'] f1.closed diff --git a/test/completion/comprehensions.py b/test/completion/comprehensions.py index 2894cf33..402ef753 100644 --- a/test/completion/comprehensions.py +++ b/test/completion/comprehensions.py @@ -210,6 +210,5 @@ d[2] next(iter({a for a in range(10)})) -# with a set literal (also doesn't work in 2.6). #? int() [a for a in {1, 2, 3}][0] diff --git a/test/completion/decorators.py b/test/completion/decorators.py index 27e455ae..043ed0bf 100644 --- a/test/completion/decorators.py +++ b/test/completion/decorators.py @@ -211,6 +211,17 @@ class 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 # ----------------- diff --git a/test/completion/dynamic_arrays.py b/test/completion/dynamic_arrays.py index 9be530fa..5cb52fc6 100644 --- a/test/completion/dynamic_arrays.py +++ b/test/completion/dynamic_arrays.py @@ -288,8 +288,6 @@ third()[0] # ----------------- # set.add # ----------------- -# Set literals are not valid in 2.6. -# python >= 2.7 st = {1.0} for a in [1,2]: st.add(a) diff --git a/test/completion/pep0484_comments.py b/test/completion/pep0484_comments.py index 7d5f7c2e..7707fcc3 100644 --- a/test/completion/pep0484_comments.py +++ b/test/completion/pep0484_comments.py @@ -47,10 +47,6 @@ b class Employee: pass -# The typing library is not installable for Python 2.6, therefore ignore the -# following tests. -# python >= 2.7 - from typing import List x = [] # type: List[Employee] #? Employee() diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index 75c1c0b0..7bee1e64 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -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 still TODO at 2016-01-23) """ -# There's no Python 2.6 typing module. -# python >= 2.7 import typing class B: pass diff --git a/test/completion/types.py b/test/completion/types.py index 2ef01f9a..8fd2c594 100644 --- a/test/completion/types.py +++ b/test/completion/types.py @@ -116,8 +116,6 @@ tup4.index # ----------------- # set # ----------------- -# Set literals are not valid in 2.6. -# python >= 2.7 set_t = {1,2} #? ['clear', 'copy'] diff --git a/test/completion/usages.py b/test/completion/usages.py index d7231b15..be27ae51 100644 --- a/test/completion/usages.py +++ b/test/completion/usages.py @@ -296,8 +296,6 @@ x = 32 [x for x in something] x = 3 -# Not supported syntax in Python 2.6. -# python >= 2.7 #< 1 (0,1), (0,10) {x:1 for x in something} #< 10 (0,1), (0,10) diff --git a/test/conftest.py b/test/conftest.py index 6e26f997..34972793 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -41,9 +41,9 @@ def parse_test_files_option(opt): opt = str(opt) if ':' in opt: (f_name, rest) = opt.split(':', 1) - return (f_name, list(map(int, rest.split(',')))) + return f_name, list(map(int, rest.split(','))) else: - return (opt, []) + return opt, [] def pytest_generate_tests(metafunc): @@ -127,7 +127,7 @@ class StaticAnalysisCase(object): @pytest.fixture() def cwd_tmpdir(monkeypatch, tmpdir): - with helpers.set_cwd(tmpdir.dirpath): + with helpers.set_cwd(tmpdir.strpath): yield tmpdir diff --git a/test/run.py b/test/run.py index 20119f99..3e523ad2 100755 --- a/test/run.py +++ b/test/run.py @@ -206,7 +206,7 @@ class IntegrationTestCase(object): completions = self.script(environment).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))) def run_goto_definitions(self, compare_cb, environment): diff --git a/test/test_api/test_api.py b/test/test_api/test_api.py index 50fa0ce3..1467f44b 100644 --- a/test/test_api/test_api.py +++ b/test/test_api/test_api.py @@ -103,8 +103,8 @@ def test_completion_on_complex_literals(Script): _check_number(Script, '4.0j.', 'complex') # No dot no completion - I thought, but 4j is actually a literall after # which a keyword like or is allowed. Good times, haha! - assert (set([c.name for c in Script('4j').completions()]) == - set(['if', 'and', 'in', 'is', 'not', 'or'])) + assert ({c.name for c in Script('4j').completions()} == + {'if', 'and', 'in', 'is', 'not', 'or'}) 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): 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): diff --git a/test/test_api/test_api_classes_follow_definition.py b/test/test_api/test_api_classes_follow_definition.py index e8342036..69bb31d0 100644 --- a/test/test_api/test_api_classes_follow_definition.py +++ b/test/test_api/test_api_classes_follow_definition.py @@ -34,7 +34,7 @@ def test_follow_import_incomplete(Script): # incomplete `from * import` part 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 ospath = check_follow_definition_types(Script, "from os.path import abspat") diff --git a/test/test_api/test_classes.py b/test/test_api/test_classes.py index b4a9e778..2580b105 100644 --- a/test/test_api/test_classes.py +++ b/test/test_api/test_classes.py @@ -68,25 +68,25 @@ def test_basedefinition_type(Script, environment): def test_basedefinition_type_import(Script): 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 - assert get_types('import t') == set(['module']) - assert get_types('import ') == set(['module']) - assert get_types('import datetime; datetime') == set(['module']) + assert get_types('import t') == {'module'} + assert get_types('import ') == {'module'} + assert get_types('import datetime; datetime') == {'module'} # from - assert get_types('from datetime import timedelta') == set(['class']) - assert get_types('from datetime import timedelta; timedelta') == set(['class']) - assert get_types('from json import tool') == set(['module']) - assert get_types('from json import tool; tool') == set(['module']) + assert get_types('from datetime import timedelta') == {'class'} + assert get_types('from datetime import timedelta; timedelta') == {'class'} + assert get_types('from json import tool') == {'module'} + assert get_types('from json import tool; tool') == {'module'} # import two levels - assert get_types('import json.tool; json') == set(['module']) - assert get_types('import json.tool; json.tool') == set(['module']) - assert get_types('import json.tool; json.tool.main') == set(['function']) - assert get_types('import json.tool') == set(['module']) - assert get_types('import json.tool', column=9) == set(['module']) + assert get_types('import json.tool; json') == {'module'} + assert get_types('import json.tool; json.tool') == {'module'} + assert get_types('import json.tool; json.tool.main') == {'function'} + assert get_types('import json.tool') == {'module'} + assert get_types('import json.tool', column=9) == {'module'} def test_function_call_signature_in_doc(Script): diff --git a/test/test_api/test_interpreter.py b/test/test_api/test_interpreter.py index a4c0311e..419eae50 100644 --- a/test/test_api/test_interpreter.py +++ b/test/test_api/test_interpreter.py @@ -16,8 +16,8 @@ else: exec source in global_map """, 'blub', 'exec')) -class _GlobalNameSpace(): - class SideEffectContainer(): +class _GlobalNameSpace: + class SideEffectContainer: pass @@ -80,7 +80,7 @@ def test_numpy_like_non_zero(): def test_nested_resolve(): - class XX(): + class XX: def x(): pass @@ -168,7 +168,7 @@ def test_list(): def test_slice(): - class Foo1(): + class Foo1: bar = [] baz = 'xbarx' _assert_interpreter_complete('getattr(Foo1, baz[1:-1]).append', @@ -177,7 +177,7 @@ def test_slice(): def test_getitem_side_effects(): - class Foo2(): + class Foo2: def __getitem__(self, index): # Possible side effects here, should therefore not call this. if True: @@ -190,7 +190,7 @@ def test_getitem_side_effects(): def test_property_error_oldstyle(): lst = [] - class Foo3(): + class Foo3: @property def bar(self): lst.append(1) @@ -261,7 +261,7 @@ def test_completion_param_annotations(): a, b, c = c.params assert a._goto_definitions() == [] 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(): @@ -269,7 +269,7 @@ def test_more_complex_instances(): def foo(self, other): return self - class Base(): + class Base: def wow(self): return Something() diff --git a/test/test_api/test_unicode.py b/test/test_api/test_unicode.py index 4d3fdf5c..b4d11012 100644 --- a/test/test_api/test_unicode.py +++ b/test/test_api/test_unicode.py @@ -63,3 +63,12 @@ def test_complete_at_zero(Script): s = Script("", 1, 0).completions() 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' diff --git a/test/test_evaluate/test_context.py b/test/test_evaluate/test_context.py index 6644ec06..116b64c8 100644 --- a/test/test_evaluate/test_context.py +++ b/test/test_evaluate/test_context.py @@ -1,7 +1,7 @@ def test_module_attributes(Script): def_, = Script('__name__').completions() assert def_.name == '__name__' - assert def_.line == None - assert def_.column == None + assert def_.line is None + assert def_.column is None str_, = def_._goto_definitions() assert str_.name == 'str' diff --git a/test/test_evaluate/test_imports.py b/test/test_evaluate/test_imports.py index e950e94b..74069f10 100644 --- a/test/test_evaluate/test_imports.py +++ b/test/test_evaluate/test_imports.py @@ -10,8 +10,6 @@ import pytest from jedi._compatibility import find_module_py33, find_module from ..helpers import cwd_at -from jedi._compatibility import is_py26 - @pytest.mark.skipif('sys.version_info < (3,3)') def test_find_module_py33(): @@ -163,10 +161,9 @@ def test_complete_on_empty_import(Script): # relative import assert 10 < len(Script("from . import classes", 1, 6, 'whatever.py').completions()) < 30 - wanted = set(['ImportError', 'import', 'ImportWarning']) - assert set([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 + wanted = {'ImportError', 'import', 'ImportWarning'} + assert {c.name for c in Script("import").completions()} == wanted + assert len(Script("import import", path='').completions()) > 0 # 111 assert Script("from datetime import").completions()[0].name == 'import' diff --git a/test/test_evaluate/test_stdlib.py b/test/test_evaluate/test_stdlib.py index f9a716b6..d2252fb0 100644 --- a/test/test_evaluate/test_stdlib.py +++ b/test/test_evaluate/test_stdlib.py @@ -5,7 +5,6 @@ with "Black Box Tests". from textwrap import dedent import pytest -from jedi._compatibility import is_py26 # The namedtuple is different for different Python2.7 versions. Some versions @@ -28,10 +27,7 @@ def test_namedtuple_str(letter, expected, Script): dave.%s""") % letter result = Script(source).completions() completions = set(r.name for r in result) - if is_py26: - assert completions == set() - else: - assert completions == set(expected) + assert completions == set(expected) def test_namedtuple_list(Script): @@ -42,10 +38,7 @@ def test_namedtuple_list(Script): garfield.l""") result = Script(source).completions() completions = set(r.name for r in result) - if is_py26: - assert completions == set() - else: - assert completions == set(['legs', 'length', 'large']) + assert completions == {'legs', 'length', 'large'} def test_namedtuple_content(Script): diff --git a/test/test_evaluate/test_sys_path.py b/test/test_evaluate/test_sys_path.py index 5bc585c7..bd966b77 100644 --- a/test/test_evaluate/test_sys_path.py +++ b/test/test_evaluate/test_sys_path.py @@ -15,9 +15,9 @@ def test_paths_from_assignment(Script): expr_stmt = script._get_module_node().children[0] 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 = ["b", 1, x + 3, y, "c"]') == set(['/foo/b', '/foo/c']) - assert paths('sys.path = a = ["a"]') == set(['/foo/a']) + assert paths('sys.path[0:0] = ["a"]') == {'/foo/a'} + assert paths('sys.path = ["b", 1, x + 3, y, "c"]') == {'/foo/b', '/foo/c'} + assert paths('sys.path = a = ["a"]') == {'/foo/a'} # Fail for complicated examples. assert paths('sys.path, other = ["a"], 2') == set() diff --git a/test/test_parso_integration/test_parser_utils.py b/test/test_parso_integration/test_parser_utils.py index e49c3f56..c207fdd6 100644 --- a/test/test_parso_integration/test_parser_utils.py +++ b/test/test_parso_integration/test_parser_utils.py @@ -7,7 +7,7 @@ from parso.python import tree import pytest -class TestCallAndName(): +class TestCallAndName: def get_call(self, source): # Get the simple_stmt and then the first one. node = parse(source).children[0] diff --git a/test/test_speed.py b/test/test_speed.py index afe0bc11..d60910ca 100644 --- a/test/test_speed.py +++ b/test/test_speed.py @@ -41,7 +41,6 @@ def test_scipy_speed(Script): s = 'import scipy.weave; scipy.weave.inline(' script = Script(s, 1, len(s), '') script.call_signatures() - #print(jedi.imports.imports_processed) @_check_speed(0.8) @@ -63,10 +62,11 @@ def test_no_repr_computation(Script): unwanted computation of repr(). Exemple : big pandas data. See issue #919. """ - class SlowRepr(): + class SlowRepr: "class to test what happens if __repr__ is very slow." def some_method(self): pass + def __repr__(self): time.sleep(0.2) test = SlowRepr() diff --git a/test/test_utils.py b/test/test_utils.py index 2eb6e8ca..2b4fd5a2 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -56,7 +56,7 @@ class TestSetupReadline(unittest.TestCase): string = 'os.path.join("a").upper' 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) finally: del self.namespace.sys @@ -68,12 +68,12 @@ class TestSetupReadline(unittest.TestCase): def test_import(self): 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'] import os 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 # items as well as items that are not only available on linux. assert len(set(self.completions(s)).symmetric_difference(goal)) < 20 @@ -85,7 +85,7 @@ class TestSetupReadline(unittest.TestCase): def test_preexisting_values(self): 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 def test_colorama(self): diff --git a/tox.ini b/tox.ini index 1cd3c4e7..bda6963b 100644 --- a/tox.ini +++ b/tox.ini @@ -24,10 +24,6 @@ setenv = env37: JEDI_TEST_ENVIRONMENT=37 commands = py.test {posargs:jedi test} -[testenv:py26] -deps = - unittest2 - {[testenv]deps} [testenv:py27] deps = # for testing the typing module