diff --git a/.travis.yml b/.travis.yml index 83dcad01..c1209a1b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,6 @@ python: env: - JEDI_TEST_ENVIRONMENT=27 - - JEDI_TEST_ENVIRONMENT=33 - JEDI_TEST_ENVIRONMENT=34 - JEDI_TEST_ENVIRONMENT=35 - JEDI_TEST_ENVIRONMENT=36 @@ -39,7 +38,6 @@ before_install: - ./travis_install.sh # Need to add the path to the Python versions in the end. This might add # something twice, but it doesn't really matter, because they are appended. - - export PATH=$PATH:/opt/python/3.3/bin - export PATH=$PATH:/opt/python/3.5/bin # 3.6 was not installed manually, but already is on the system. However # it's not on path (unless 3.6 is selected). diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8069a1c6..bdc503a2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,9 +3,11 @@ Changelog --------- -0.12.2 () +0.13.0 () +++++++++++++++++++ +- Remove Python 3.3 support. Python 3.3 support has been dropped by the Python + foundation. - Added ``include_builtins`` as a parameter to usages. - ``goto_assignments`` has a new ``follow_builtin_imports`` parameter that changes the previous behavior slightly. diff --git a/README.rst b/README.rst index 08dbd5f5..64c7f903 100644 --- a/README.rst +++ b/README.rst @@ -111,8 +111,8 @@ understands, see: `Features `_. A list of caveats can be found on the same page. -You can run Jedi on CPython 2.7 or 3.3+ but it should also -understand/parse code older than those versions. Additonally you should be able +You can run Jedi on CPython 2.7 or 3.4+ but it should also +understand/parse code older than those versions. Additionally you should be able to use `Virtualenvs `_ very well. diff --git a/appveyor.yml b/appveyor.yml index 52c2bc42..9ef41100 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,9 +3,6 @@ environment: - TOXENV: py27 PYTHON_PATH: C:\Python27 JEDI_TEST_ENVIRONMENT: 27 - - TOXENV: py27 - PYTHON_PATH: C:\Python27 - JEDI_TEST_ENVIRONMENT: 33 - TOXENV: py27 PYTHON_PATH: C:\Python27 JEDI_TEST_ENVIRONMENT: 34 @@ -19,9 +16,6 @@ environment: - TOXENV: py34 PYTHON_PATH: C:\Python34 JEDI_TEST_ENVIRONMENT: 27 - - TOXENV: py34 - PYTHON_PATH: C:\Python34 - JEDI_TEST_ENVIRONMENT: 33 - TOXENV: py34 PYTHON_PATH: C:\Python34 JEDI_TEST_ENVIRONMENT: 34 @@ -35,9 +29,6 @@ environment: - TOXENV: py35 PYTHON_PATH: C:\Python35 JEDI_TEST_ENVIRONMENT: 27 - - TOXENV: py35 - PYTHON_PATH: C:\Python35 - JEDI_TEST_ENVIRONMENT: 33 - TOXENV: py35 PYTHON_PATH: C:\Python35 JEDI_TEST_ENVIRONMENT: 34 @@ -51,9 +42,6 @@ environment: - TOXENV: py36 PYTHON_PATH: C:\Python36 JEDI_TEST_ENVIRONMENT: 27 - - TOXENV: py36 - PYTHON_PATH: C:\Python36 - JEDI_TEST_ENVIRONMENT: 33 - TOXENV: py36 PYTHON_PATH: C:\Python36 JEDI_TEST_ENVIRONMENT: 34 diff --git a/conftest.py b/conftest.py index d7175e24..1b49c2b9 100644 --- a/conftest.py +++ b/conftest.py @@ -6,8 +6,7 @@ from functools import partial import pytest import jedi -from jedi.api.environment import get_default_environment, \ - get_system_environment, InterpreterEnvironment +from jedi.api.environment import get_system_environment, InterpreterEnvironment from jedi._compatibility import py_version collect_ignore = [ @@ -98,9 +97,6 @@ def environment(request): if version is None: version = os.environ.get('JEDI_TEST_ENVIRONMENT', str(py_version)) - if int(version) == py_version: - return get_default_environment() - return get_system_environment(version[0] + '.' + version[1:]) diff --git a/docs/docs/features.rst b/docs/docs/features.rst index 9a3cb052..d474b591 100644 --- a/docs/docs/features.rst +++ b/docs/docs/features.rst @@ -20,7 +20,7 @@ make it work. General Features ---------------- -- Python 2.7 and 3.3+ support +- Python 2.7 and 3.4+ support - Ignores syntax errors and wrong indentation - Can deal with complex module / function / class structures - Great Virtualenv support diff --git a/jedi/_compatibility.py b/jedi/_compatibility.py index b5dde6a4..9b5edc86 100644 --- a/jedi/_compatibility.py +++ b/jedi/_compatibility.py @@ -16,8 +16,6 @@ except ImportError: pass 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_py35 = is_py3 and sys.version_info[1] >= 5 py_version = int(str(sys.version_info[0]) + str(sys.version_info[1])) @@ -116,7 +114,7 @@ def find_module_py33(string, path=None, loader=None, full_name=None, is_global_s return module_file, module_path, is_package -def find_module_pre_py33(string, path=None, full_name=None, is_global_search=True): +def find_module_pre_py34(string, path=None, full_name=None, is_global_search=True): # This import is here, because in other places it will raise a # DeprecationWarning. import imp @@ -151,8 +149,7 @@ def find_module_pre_py33(string, path=None, full_name=None, is_global_search=Tru raise ImportError("No module named {}".format(string)) -find_module = find_module_py33 if is_py33 else find_module_pre_py33 -find_module = find_module_py34 if is_py34 else find_module +find_module = find_module_py34 if is_py3 else find_module_pre_py34 find_module.__doc__ = """ Provides information about a module. @@ -369,6 +366,7 @@ def print_to_stderr(*args): eval("print(*args, file=sys.stderr)") else: print >> sys.stderr, args + sys.stderr.flush() def utf8_repr(func): @@ -521,6 +519,9 @@ class GeneralizedPopen(subprocess.Popen): except AttributeError: CREATE_NO_WINDOW = 0x08000000 kwargs['creationflags'] = CREATE_NO_WINDOW + # The child process doesn't need file descriptors except 0, 1, 2. + # This is unix only. + kwargs['close_fds'] = 'posix' in sys.builtin_module_names super(GeneralizedPopen, self).__init__(*args, **kwargs) diff --git a/jedi/evaluate/compiled/access.py b/jedi/evaluate/compiled/access.py index 610cec92..ab1d3545 100644 --- a/jedi/evaluate/compiled/access.py +++ b/jedi/evaluate/compiled/access.py @@ -5,7 +5,7 @@ from textwrap import dedent import operator as op from collections import namedtuple -from jedi._compatibility import unicode, is_py3, is_py34, builtins, \ +from jedi._compatibility import unicode, is_py3, builtins, \ py_version, force_unicode, print_to_stderr from jedi.evaluate.compiled.getattr_static import getattr_static @@ -31,10 +31,9 @@ NOT_CLASS_TYPES = ( if is_py3: NOT_CLASS_TYPES += ( types.MappingProxyType, - types.SimpleNamespace + types.SimpleNamespace, + types.DynamicClassAttribute, ) - if is_py34: - NOT_CLASS_TYPES += (types.DynamicClassAttribute,) # Those types don't exist in typing. @@ -141,7 +140,7 @@ def load_module(evaluator, dotted_name, sys_path): __import__(dotted_name) except ImportError: # If a module is "corrupt" or not really a Python module or whatever. - print_to_stderr('Module %s not importable.' % dotted_name) + print_to_stderr('Module %s not importable in path %s.' % (dotted_name, sys_path)) return None except Exception: # Since __import__ pretty much makes code execution possible, just diff --git a/jedi/evaluate/compiled/context.py b/jedi/evaluate/compiled/context.py index ea59341e..52c0bb54 100644 --- a/jedi/evaluate/compiled/context.py +++ b/jedi/evaluate/compiled/context.py @@ -447,8 +447,11 @@ def create_from_name(evaluator, compiled_object, name): pass access = compiled_object.access_handle.getattr(name, default=None) + parent_context = compiled_object + if parent_context.is_class(): + parent_context = parent_context.parent_context return create_cached_compiled_object( - evaluator, access, parent_context=compiled_object, faked=faked + evaluator, access, parent_context=parent_context, faked=faked ) diff --git a/jedi/evaluate/compiled/subprocess/__init__.py b/jedi/evaluate/compiled/subprocess/__init__.py index ddf38bf7..5a9a52fd 100644 --- a/jedi/evaluate/compiled/subprocess/__init__.py +++ b/jedi/evaluate/compiled/subprocess/__init__.py @@ -15,9 +15,15 @@ import errno import weakref import traceback from functools import partial +from threading import Thread +try: + from queue import Queue, Empty +except ImportError: + from Queue import Queue, Empty # python 2.7 from jedi._compatibility import queue, is_py3, force_unicode, \ pickle_dump, pickle_load, GeneralizedPopen +from jedi import debug from jedi.cache import memoize_method from jedi.evaluate.compiled.subprocess import functions from jedi.evaluate.compiled.access import DirectObjectAccess, AccessPath, \ @@ -28,6 +34,12 @@ from jedi.api.exceptions import InternalError _MAIN_PATH = os.path.join(os.path.dirname(__file__), '__main__.py') +def _enqueue_output(out, queue): + for line in iter(out.readline, b''): + queue.put(line) + out.close() + + def _get_function(name): return getattr(functions, name) @@ -135,6 +147,7 @@ class CompiledSubprocess(object): @property @memoize_method def _process(self): + debug.dbg('Start environment subprocess %s', self._executable) parso_path = sys.modules['parso'].__file__ args = ( self._executable, @@ -142,7 +155,7 @@ class CompiledSubprocess(object): os.path.dirname(os.path.dirname(parso_path)), '.'.join(str(x) for x in sys.version_info[:3]), ) - return GeneralizedPopen( + process = GeneralizedPopen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, @@ -151,6 +164,14 @@ class CompiledSubprocess(object): # (this is already the case on Python 3). bufsize=-1 ) + self._stderr_queue = Queue() + self._stderr_thread = t = Thread( + target=_enqueue_output, + args=(process.stderr, self._stderr_queue) + ) + t.daemon = True + t.start() + return process def run(self, evaluator, function, args=(), kwargs={}): # Delete old evaluators. @@ -219,6 +240,16 @@ class CompiledSubprocess(object): stderr, )) + while True: + # Try to do some error reporting from the subprocess and print its + # stderr contents. + try: + line = self._stderr_queue.get_nowait() + line = line.decode('utf-8', 'replace') + debug.warning('stderr output: %s' % line.rstrip('\n')) + except Empty: + break + if is_exception: # Replace the attribute error message with a the traceback. It's # way more informative. @@ -282,11 +313,9 @@ class Listener(object): def listen(self): stdout = sys.stdout - # Mute stdout/stderr. Nobody should actually be able to write to those, - # because stdout is used for IPC and stderr will just be annoying if it - # leaks (on module imports). + # Mute stdout. Nobody should actually be able to write to it, + # because stdout is used for IPC. sys.stdout = open(os.devnull, 'w') - sys.stderr = open(os.devnull, 'w') stdin = sys.stdin if sys.version_info[0] > 2: stdout = stdout.buffer diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index 21e19e11..0ce52407 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -11,7 +11,7 @@ from jedi.evaluate.arguments import AbstractArguments, AnonymousArguments from jedi.cache import memoize_method from jedi.evaluate.context.function import FunctionExecutionContext, \ FunctionContext, AbstractFunction -from jedi.evaluate.context.klass import ClassContext, apply_py__get__ +from jedi.evaluate.context.klass import ClassContext, apply_py__get__, ClassFilter from jedi.evaluate.context import iterable from jedi.parser_utils import get_parent_scope @@ -87,7 +87,7 @@ class AbstractInstanceContext(Context): def execute_function_slots(self, names, *evaluated_args): return ContextSet.from_sets( - name.execute_evaluated(*evaluated_args) + name.infer().execute_evaluated(*evaluated_args) for name in names ) @@ -169,7 +169,7 @@ class AbstractInstanceContext(Context): def create_init_executions(self): for name in self.get_function_slot_names(u'__init__'): - if isinstance(name, SelfName): + if isinstance(name, LazyInstanceClassName): function = FunctionContext.from_context( self.parent_context, name.tree_name.parent @@ -265,17 +265,23 @@ class AnonymousInstance(TreeInstance): class CompiledInstanceName(compiled.CompiledName): - def __init__(self, evaluator, instance, parent_context, name): - super(CompiledInstanceName, self).__init__(evaluator, parent_context, name) + + def __init__(self, evaluator, instance, klass, name): + super(CompiledInstanceName, self).__init__( + evaluator, + klass.parent_context, + name.string_name + ) self._instance = instance + self._class = klass + self._class_member_name = name @iterator_to_context_set def infer(self): - for result_context in super(CompiledInstanceName, self).infer(): + for result_context in self._class_member_name.infer(): is_function = result_context.api_type == 'function' if result_context.tree_node is not None and is_function: - - yield BoundMethod(self._instance, self.parent_context, result_context) + yield BoundMethod(self._instance, self._class, result_context) else: if is_function: yield CompiledBoundMethod(result_context) @@ -283,20 +289,26 @@ class CompiledInstanceName(compiled.CompiledName): yield result_context -class CompiledInstanceClassFilter(compiled.CompiledObjectFilter): +class CompiledInstanceClassFilter(filters.AbstractFilter): name_class = CompiledInstanceName - def __init__(self, evaluator, instance, compiled_object): - super(CompiledInstanceClassFilter, self).__init__( - evaluator, - compiled_object, - is_instance=True, - ) + def __init__(self, evaluator, instance, klass): + self._evaluator = evaluator self._instance = instance + self._class = klass + self._class_filter = next(klass.get_filters(is_instance=True)) - def _create_name(self, name): - return self.name_class( - self._evaluator, self._instance, self._compiled_object, name) + def get(self, name): + return self._convert(self._class_filter.get(name)) + + def values(self): + return self._convert(self._class_filter.values()) + + def _convert(self, names): + return [ + CompiledInstanceName(self._evaluator, self._instance, self._class, n) + for n in names + ] class BoundMethod(AbstractFunction): @@ -332,11 +344,6 @@ class CompiledBoundMethod(compiled.CompiledObject): return list(super(CompiledBoundMethod, self).get_param_names())[1:] -class InstanceNameDefinition(filters.TreeNameDefinition): - def infer(self): - return super(InstanceNameDefinition, self).infer() - - class SelfName(filters.TreeNameDefinition): """ This name calculates the parent_context lazily. @@ -351,10 +358,15 @@ class SelfName(filters.TreeNameDefinition): return self._instance.create_instance_context(self.class_context, self.tree_name) -class LazyInstanceClassName(SelfName): +class LazyInstanceClassName(object): + def __init__(self, instance, class_context, class_member_name): + self._instance = instance + self.class_context = class_context + self._class_member_name = class_member_name + @iterator_to_context_set def infer(self): - for result_context in super(LazyInstanceClassName, self).infer(): + for result_context in self._class_member_name.infer(): if isinstance(result_context, FunctionContext): # Classes are never used to resolve anything within the # functions. Only other functions and modules will resolve @@ -364,45 +376,51 @@ class LazyInstanceClassName(SelfName): for c in apply_py__get__(result_context, self._instance): yield c + def __getattr__(self, name): + return getattr(self._class_member_name, name) -class InstanceClassFilter(filters.ParserTreeFilter): - name_class = LazyInstanceClassName +class InstanceClassFilter(filters.AbstractFilter): + """ + This filter is special in that it uses the class filter and wraps the + resulting names in LazyINstanceClassName. The idea is that the class name + filtering can be very flexible and always be reflected in instances. + """ def __init__(self, evaluator, context, class_context, origin_scope): - super(InstanceClassFilter, self).__init__( - evaluator=evaluator, - context=context, - node_context=class_context, - origin_scope=origin_scope - ) + self._instance = context self._class_context = class_context + self._class_filter = next(class_context.get_filters( + search_global=False, + origin_scope=origin_scope, + is_instance=True, + )) - def _equals_origin_scope(self): - node = self._origin_scope - while node is not None: - if node == self._parser_scope or node == self.context: - return True - node = get_parent_scope(node) - return False + def get(self, name): + return self._convert(self._class_filter.get(name)) - def _access_possible(self, name): - return not name.value.startswith('__') or name.value.endswith('__') \ - or self._equals_origin_scope() + def values(self): + return self._convert(self._class_filter.values()) - def _filter(self, names): - names = super(InstanceClassFilter, self)._filter(names) - return [name for name in names if self._access_possible(name)] - - def _convert_names(self, names): - return [self.name_class(self.context, self._class_context, name) for name in names] + def _convert(self, names): + return [LazyInstanceClassName(self._instance, self._class_context, n) for n in names] -class SelfAttributeFilter(InstanceClassFilter): +class SelfAttributeFilter(ClassFilter): """ This class basically filters all the use cases where `self.*` was assigned. """ name_class = SelfName + def __init__(self, evaluator, context, class_context, origin_scope): + super(SelfAttributeFilter, self).__init__( + evaluator=evaluator, + context=context, + node_context=class_context, + origin_scope=origin_scope, + is_instance=True, + ) + self._class_context = class_context + def _filter(self, names): names = self._filter_self_names(names) if isinstance(self._parser_scope, compiled.CompiledObject) and False: @@ -421,6 +439,9 @@ class SelfAttributeFilter(InstanceClassFilter): if name.is_definition() and self._access_possible(name): yield name + def _convert_names(self, names): + return [self.name_class(self.context, self._class_context, name) for name in names] + def _check_flows(self, names): return names diff --git a/jedi/evaluate/context/klass.py b/jedi/evaluate/context/klass.py index 93ccf99d..3343ec07 100644 --- a/jedi/evaluate/context/klass.py +++ b/jedi/evaluate/context/klass.py @@ -38,6 +38,7 @@ py__doc__(include_call_signature: Returns the docstring for a context. """ from jedi._compatibility import use_metaclass +from jedi.parser_utils import get_parent_scope from jedi.evaluate.cache import evaluator_method_cache, CachedMetaClass from jedi.evaluate import compiled from jedi.evaluate.lazy_context import LazyKnownContext @@ -58,9 +59,10 @@ def apply_py__get__(context, base_context): class ClassName(TreeNameDefinition): - def __init__(self, parent_context, tree_name, name_context): + def __init__(self, parent_context, tree_name, name_context, apply_decorators): super(ClassName, self).__init__(parent_context, tree_name) self._name_context = name_context + self._apply_decorators = apply_decorators @iterator_to_context_set def infer(self): @@ -70,16 +72,45 @@ class ClassName(TreeNameDefinition): self.parent_context.evaluator, self._name_context, self.tree_name) for result_context in inferred: - for c in apply_py__get__(result_context, self.parent_context): - yield c + if self._apply_decorators: + for c in apply_py__get__(result_context, self.parent_context): + yield c + else: + yield result_context class ClassFilter(ParserTreeFilter): name_class = ClassName + def __init__(self, *args, **kwargs): + self._is_instance = kwargs.pop('is_instance') # Python 2 :/ + super(ClassFilter, self).__init__(*args, **kwargs) + def _convert_names(self, names): - return [self.name_class(self.context, name, self._node_context) - for name in names] + return [ + self.name_class( + parent_context=self.context, + tree_name=name, + name_context=self._node_context, + apply_decorators=not self._is_instance, + ) for name in names + ] + + def _equals_origin_scope(self): + node = self._origin_scope + while node is not None: + if node == self._parser_scope or node == self.context: + return True + node = get_parent_scope(node) + return False + + def _access_possible(self, name): + return not name.value.startswith('__') or name.value.endswith('__') \ + or self._equals_origin_scope() + + def _filter(self, names): + names = super(ClassFilter, self)._filter(names) + return [name for name in names if self._access_possible(name)] class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): @@ -163,7 +194,9 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): else: yield ClassFilter( self.evaluator, self, node_context=cls, - origin_scope=origin_scope) + origin_scope=origin_scope, + is_instance=is_instance + ) def is_class(self): return True diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index 4294f2a6..fd41b55a 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -38,12 +38,6 @@ class AbstractNameDefinition(object): return '<%s: %s>' % (self.__class__.__name__, self.string_name) return '<%s: %s@%s>' % (self.__class__.__name__, self.string_name, self.start_pos) - def execute(self, arguments): - return self.infer().execute(arguments) - - def execute_evaluated(self, *args, **kwargs): - return self.infer().execute_evaluated(*args, **kwargs) - def is_import(self): return False diff --git a/setup.py b/setup.py index 18a3b44f..ebcd4e55 100755 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ setup(name='jedi', keywords='python completion refactoring vim', long_description=readme, packages=find_packages(exclude=['test', 'test.*']), - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*', + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', install_requires=install_requires, extras_require={'dev': ['docopt']}, package_data={'jedi': ['evaluate/compiled/fake/*.pym']}, @@ -43,7 +43,6 @@ setup(name='jedi', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', diff --git a/test/completion/arrays.py b/test/completion/arrays.py index 302349d3..ffbf2337 100644 --- a/test/completion/arrays.py +++ b/test/completion/arrays.py @@ -437,7 +437,7 @@ def test_func(): #? int() tuple({1})[0] -# python >= 3.3 +# python >= 3.4 # ----------------- # PEP 3132 Extended Iterable Unpacking (star unpacking) # ----------------- diff --git a/test/completion/classes.py b/test/completion/classes.py index 92b8dc90..1c192370 100644 --- a/test/completion/classes.py +++ b/test/completion/classes.py @@ -417,6 +417,9 @@ class PrivateVar(): def __private_func(self): return 1 + #? int() + __private_func() + def wrap_private(self): return self.__private_func() #? [] @@ -425,6 +428,8 @@ PrivateVar().__var PrivateVar().__var #? [] PrivateVar().__private_func +#? [] +PrivateVar.__private_func #? int() PrivateVar().wrap_private() diff --git a/test/completion/generators.py b/test/completion/generators.py index 69e4acdb..ee541df1 100644 --- a/test/completion/generators.py +++ b/test/completion/generators.py @@ -220,7 +220,7 @@ def x(): # yield from # ----------------- -# python >= 3.3 +# python >= 3.4 def yield_from(): yield from iter([1]) diff --git a/test/completion/pep0484_basic.py b/test/completion/pep0484_basic.py index 0230d375..6c88804d 100644 --- a/test/completion/pep0484_basic.py +++ b/test/completion/pep0484_basic.py @@ -1,6 +1,6 @@ """ Pep-0484 type hinting """ -# python >= 3.2 +# python >= 3.4 class A(): diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index 123e6438..58037934 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -243,7 +243,7 @@ for key in x.keys(): for value in x.values(): #? int() value -# python >= 3.2 +# python >= 3.4 class TestDefaultDict(typing.DefaultDict[str, int]): def setdud(self): @@ -271,7 +271,7 @@ for key in x.keys(): for value in x.values(): #? int() value -# python >= 3.2 +# python >= 3.4 """ diff --git a/test/test_api/test_call_signatures.py b/test/test_api/test_call_signatures.py index 77a58005..ccd8fb23 100644 --- a/test/test_api/test_call_signatures.py +++ b/test/test_api/test_call_signatures.py @@ -6,7 +6,7 @@ import pytest from ..helpers import TestCase from jedi import cache -from jedi._compatibility import is_py33 +from jedi._compatibility import is_py3 def assert_signature(Script, source, expected_name, expected_index=0, line=None, column=None): @@ -247,7 +247,7 @@ def _params(Script, source, line=None, column=None): def test_param_name(Script): - if not is_py33: + if not is_py3: p = _params(Script, '''int(''') # int is defined as: `int(x[, base])` assert p[0].name == 'x' diff --git a/test/test_evaluate/test_pyc.py b/test/test_evaluate/test_pyc.py index d31254cc..ac4c6996 100644 --- a/test/test_evaluate/test_pyc.py +++ b/test/test_evaluate/test_pyc.py @@ -10,8 +10,10 @@ import os import shutil import sys +import pytest + import jedi -from ..helpers import cwd_at +from jedi.api.environment import SameEnvironment SRC = """class Foo: @@ -22,35 +24,44 @@ class Bar: """ -def generate_pyc(): - os.mkdir("dummy_package") - with open("dummy_package/__init__.py", 'w'): +@pytest.fixture +def pyc_project_path(tmpdir): + path = tmpdir.strpath + dummy_package_path = os.path.join(path, "dummy_package") + os.mkdir(dummy_package_path) + with open(os.path.join(dummy_package_path, "__init__.py"), 'w'): pass - with open("dummy_package/dummy.py", 'w') as f: + + dummy_path = os.path.join(dummy_package_path, 'dummy.py') + with open(dummy_path, 'w') as f: f.write(SRC) import compileall - compileall.compile_file("dummy_package/dummy.py") - os.remove("dummy_package/dummy.py") + compileall.compile_file(dummy_path) + os.remove(dummy_path) - if sys.version_info[0] == 3: + if sys.version_info.major == 3: # Python3 specific: # To import pyc modules, we must move them out of the __pycache__ # directory and rename them to remove ".cpython-%s%d" # see: http://stackoverflow.com/questions/11648440/python-does-not-detect-pyc-files - for f in os.listdir("dummy_package/__pycache__"): + pycache = os.path.join(dummy_package_path, "__pycache__") + for f in os.listdir(pycache): dst = f.replace('.cpython-%s%s' % sys.version_info[:2], "") - dst = os.path.join("dummy_package", dst) - shutil.copy(os.path.join("dummy_package/__pycache__", f), dst) + dst = os.path.join(dummy_package_path, dst) + shutil.copy(os.path.join(pycache, f), dst) + try: + yield path + finally: + shutil.rmtree(path) -@cwd_at('test/test_evaluate') -def test_pyc(Script): +def test_pyc(pyc_project_path): """ The list of completion must be greater than 2. """ - try: - generate_pyc() - s = jedi.Script("from dummy_package import dummy; dummy.", path='blub.py') - assert len(s.completions()) >= 2 - finally: - shutil.rmtree("dummy_package") + path = os.path.join(pyc_project_path, 'blub.py') + s = jedi.Script( + "from dummy_package import dummy; dummy.", + path=path, + environment=SameEnvironment()) + assert len(s.completions()) >= 2 diff --git a/tox.ini b/tox.ini index 9d247acd..f450b735 100644 --- a/tox.ini +++ b/tox.ini @@ -1,8 +1,8 @@ [tox] -envlist = py27, py33, py34, py35, py36 +envlist = py27, py34, py35, py36 [testenv] deps = - pytest>=2.3.5, < 3.3 + pytest>=2.3.5 pytest-cache # docopt for sith doctests docopt @@ -19,7 +19,6 @@ setenv = # To test Jedi in different versions than the same Python version, set a # different test environment. env27: JEDI_TEST_ENVIRONMENT=27 - env33: JEDI_TEST_ENVIRONMENT=33 env34: JEDI_TEST_ENVIRONMENT=34 env35: JEDI_TEST_ENVIRONMENT=35 env36: JEDI_TEST_ENVIRONMENT=36 @@ -33,10 +32,6 @@ deps = # numpydoc for typing scipy stack numpydoc {[testenv]deps} -[testenv:py33] -deps = - typing - {[testenv]deps} [testenv:py34] deps = typing diff --git a/travis_install.sh b/travis_install.sh index 1142ac3a..2c5eb944 100755 --- a/travis_install.sh +++ b/travis_install.sh @@ -8,10 +8,6 @@ set -e sudo chown root: /opt/python/3.6/bin/python sudo chown root: /opt/python/3.6.3/bin/python -if [[ $JEDI_TEST_ENVIRONMENT == "33" ]]; then - VERSION=3.3 - DOWNLOAD=1 -fi if [[ $JEDI_TEST_ENVIRONMENT == "35" ]]; then VERSION=3.5 DOWNLOAD=1