diff --git a/jedi/api/environment.py b/jedi/api/environment.py index 77b0a4be..38c546b9 100644 --- a/jedi/api/environment.py +++ b/jedi/api/environment.py @@ -17,7 +17,7 @@ import parso _VersionInfo = namedtuple('VersionInfo', 'major minor micro') -_SUPPORTED_PYTHONS = ['3.6', '3.5', '3.4', '3.3', '2.7'] +_SUPPORTED_PYTHONS = ['3.7', '3.6', '3.5', '3.4', '3.3', '2.7'] _SAFE_PATHS = ['/usr/bin', '/usr/local/bin'] _CURRENT_VERSION = '%s.%s' % (sys.version_info.major, sys.version_info.minor) @@ -147,13 +147,18 @@ class InterpreterEnvironment(_BaseEnvironment): def _get_virtual_env_from_var(): + """Get virtualenv environment from VIRTUAL_ENV environment variable. + + It uses `safe=False` with ``create_environment``, because the environment + variable is considered to be safe / controlled by the user solely. + """ var = os.environ.get('VIRTUAL_ENV') if var is not None: if var == sys.prefix: return SameEnvironment() try: - return create_environment(var) + return create_environment(var, safe=False) except InvalidPythonEnvironment: pass @@ -318,10 +323,10 @@ def _get_executables_from_windows_registry(version): # TODO: support Python Anaconda. sub_keys = [ - r'SOFTWARE\Python\PythonCore\{version}\InstallPath', - r'SOFTWARE\Wow6432Node\Python\PythonCore\{version}\InstallPath', - r'SOFTWARE\Python\PythonCore\{version}-32\InstallPath', - r'SOFTWARE\Wow6432Node\Python\PythonCore\{version}-32\InstallPath' + r'SOFTWARE\Python\PythonCore\{version}\InstallPath', + r'SOFTWARE\Wow6432Node\Python\PythonCore\{version}\InstallPath', + r'SOFTWARE\Python\PythonCore\{version}-32\InstallPath', + r'SOFTWARE\Wow6432Node\Python\PythonCore\{version}-32\InstallPath' ] for root_key in [winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE]: for sub_key in sub_keys: diff --git a/jedi/evaluate/context/iterable.py b/jedi/evaluate/context/iterable.py index a12cef7c..b43ede8a 100644 --- a/jedi/evaluate/context/iterable.py +++ b/jedi/evaluate/context/iterable.py @@ -358,21 +358,39 @@ class SequenceLiteralContext(Sequence): return [] # Direct closing bracket, doesn't contain items. if array_node.type == 'testlist_comp': - return array_node.children[::2] + # filter out (for now) pep 448 single-star unpacking + return [value for value in array_node.children[::2] + if value.type != "star_expr"] elif array_node.type == 'dictorsetmaker': kv = [] iterator = iter(array_node.children) for key in iterator: - op = next(iterator, None) - if op is None or op == ',': - kv.append(key) # A set. - else: - assert op == ':' # A dict. - kv.append((key, next(iterator))) + if key == "**": + # dict with pep 448 double-star unpacking + # for now ignoring the values imported by ** + next(iterator) next(iterator, None) # Possible comma. + else: + op = next(iterator, None) + if op is None or op == ',': + if key.type == "star_expr": + # pep 448 single-star unpacking + # for now ignoring values imported by * + pass + else: + kv.append(key) # A set. + else: + assert op == ':' # A dict. + kv.append((key, next(iterator))) + next(iterator, None) # Possible comma. return kv else: - return [array_node] + if array_node.type == "star_expr": + # pep 448 single-star unpacking + # for now ignoring values imported by * + return [] + else: + return [array_node] def exact_key_items(self): """ diff --git a/jedi/evaluate/syntax_tree.py b/jedi/evaluate/syntax_tree.py index 9e5cec41..0a553729 100644 --- a/jedi/evaluate/syntax_tree.py +++ b/jedi/evaluate/syntax_tree.py @@ -264,7 +264,8 @@ def eval_atom(context, atom): array_node_c = array_node.children except AttributeError: array_node_c = [] - if c[0] == '{' and (array_node == '}' or ':' in array_node_c): + if c[0] == '{' and (array_node == '}' or ':' in array_node_c or + '**' in array_node_c): context = iterable.DictLiteralContext(context.evaluator, context, atom) else: context = iterable.SequenceLiteralContext(context.evaluator, context, atom) diff --git a/setup.py b/setup.py index ebcd4e55..d964d541 100755 --- a/setup.py +++ b/setup.py @@ -31,7 +31,15 @@ setup(name='jedi', packages=find_packages(exclude=['test', 'test.*']), python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', install_requires=install_requires, - extras_require={'dev': ['docopt']}, + extras_require={ + 'testing': [ + 'pytest>=2.3.5', + # docopt for sith doctests + 'docopt', + # coloroma for colored debug output + 'colorama', + ], + }, package_data={'jedi': ['evaluate/compiled/fake/*.pym']}, platforms=['any'], classifiers=[ diff --git a/test/completion/types.py b/test/completion/types.py index 19621b29..f0c4c838 100644 --- a/test/completion/types.py +++ b/test/completion/types.py @@ -131,3 +131,46 @@ set_t2 = set() #? ['clear', 'copy'] set_t2.c + +# ----------------- +# pep 448 unpacking generalizations +# ----------------- +# python >= 3.5 + +d = {'a': 3} + +#? dict() +{**d} + +#? str() +{**d, "b": "b"}["b"] + +# Should resolve to int() but jedi is not smart enough yet +# Here to make sure it doesn't result in crash though +#? +{**d}["a"] + +s = {1, 2, 3} + +#? set() +{*s} + +#? set() +{*s, 4, *s} + +s = {1, 2, 3} +# Should resolve to int() but jedi is not smart enough yet +# Here to make sure it doesn't result in crash though +#? +{*s}.pop() + +#? int() +{*s, 4}.pop() + +# Should resolve to int() but jedi is not smart enough yet +# Here to make sure it doesn't result in crash though +#? +[*s][0] + +#? int() +[*s, 4][0] diff --git a/test/conftest.py b/test/conftest.py index 0a5598e3..7291600a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -126,11 +126,12 @@ class StaticAnalysisCase(object): return "<%s: %s>" % (self.__class__.__name__, os.path.basename(self._path)) -@pytest.fixture() -def venv_path(tmpdir, environment): +@pytest.fixture(scope='session') +def venv_path(tmpdir_factory, environment): if environment.version_info.major < 3: pytest.skip("python -m venv does not exist in Python 2") + tmpdir = tmpdir_factory.mktemp('venv_path') dirname = os.path.join(tmpdir.dirname, 'venv') # We cannot use the Python from tox because tox creates virtualenvs and diff --git a/test/test_api/test_environment.py b/test/test_api/test_environment.py index a11e4857..98230e57 100644 --- a/test/test_api/test_environment.py +++ b/test/test_api/test_environment.py @@ -110,3 +110,22 @@ def test_create_environment_venv_path(venv_path): def test_create_environment_executable(): environment = create_environment(sys.executable) assert environment.executable == sys.executable + + +def test_get_default_environment_from_env_does_not_use_safe(tmpdir, monkeypatch): + fake_python = os.path.join(str(tmpdir), 'fake_python') + with open(fake_python, 'w') as f: + f.write('') + + def _get_subprocess(self): + if self._start_executable != fake_python: + raise RuntimeError('Should not get called!') + self.executable = fake_python + self.path = 'fake' + + monkeypatch.setattr('jedi.api.environment.Environment._get_subprocess', + _get_subprocess) + + monkeypatch.setenv('VIRTUAL_ENV', fake_python) + env = get_default_environment() + assert env.path == 'fake' diff --git a/tox.ini b/tox.ini index f450b735..92d61f87 100644 --- a/tox.ini +++ b/tox.ini @@ -1,16 +1,10 @@ [tox] envlist = py27, py34, py35, py36 [testenv] -deps = - pytest>=2.3.5 - pytest-cache -# docopt for sith doctests - docopt -# coloroma for colored debug output - colorama +extras = testing # Overwrite the parso version (only used sometimes). -# git+https://github.com/davidhalter/parso.git - -rrequirements.txt +# deps = +# git+https://github.com/davidhalter/parso.git passenv = JEDI_TEST_ENVIRONMENT setenv = # https://github.com/tomchristie/django-rest-framework/issues/1957 @@ -24,34 +18,29 @@ setenv = env36: JEDI_TEST_ENVIRONMENT=36 env37: JEDI_TEST_ENVIRONMENT=37 commands = - py.test {posargs:jedi test} + pytest {posargs:jedi test} [testenv:py27] deps = # for testing the typing module typing # numpydoc for typing scipy stack numpydoc - {[testenv]deps} [testenv:py34] deps = typing numpydoc - {[testenv]deps} [testenv:py35] deps = numpydoc - {[testenv]deps} [testenv:py36] deps = numpydoc - {[testenv]deps} [testenv:cov] deps = coverage numpydoc - {[testenv]deps} commands = - coverage run --source jedi -m py.test + coverage run --source jedi -m pytest coverage report [testenv:sith] commands =