Merge branch 'python3'

This commit is contained in:
Dave Halter
2020-07-17 21:58:26 +02:00
146 changed files with 1054 additions and 2369 deletions
-4
View File
@@ -5,15 +5,11 @@ python:
- 3.8 - 3.8
- 3.7 - 3.7
- 3.6 - 3.6
- 3.5
- 2.7
env: env:
- JEDI_TEST_ENVIRONMENT=38 - JEDI_TEST_ENVIRONMENT=38
- JEDI_TEST_ENVIRONMENT=37 - JEDI_TEST_ENVIRONMENT=37
- JEDI_TEST_ENVIRONMENT=36 - JEDI_TEST_ENVIRONMENT=36
- JEDI_TEST_ENVIRONMENT=35
- JEDI_TEST_ENVIRONMENT=27
- JEDI_TEST_ENVIRONMENT=interpreter - JEDI_TEST_ENVIRONMENT=interpreter
matrix: matrix:
+1 -1
View File
@@ -98,7 +98,7 @@ Features and Limitations
Jedi's features are listed here: Jedi's features are listed here:
`Features <https://jedi.readthedocs.org/en/latest/docs/features.html>`_. `Features <https://jedi.readthedocs.org/en/latest/docs/features.html>`_.
You can run Jedi on CPython 2.7 or 3.5+ but it should also You can run Jedi on Python 3.6+ but it should also
understand code that is older than those versions. Additionally you should be understand code that is older than those versions. Additionally you should be
able to use `Virtualenvs <https://jedi.readthedocs.org/en/latest/docs/api.html#environments>`_ able to use `Virtualenvs <https://jedi.readthedocs.org/en/latest/docs/api.html#environments>`_
very well. very well.
-38
View File
@@ -6,12 +6,6 @@ environment:
- TOXENV: py37 - TOXENV: py37
PYTHON_PATH: C:\Python37 PYTHON_PATH: C:\Python37
JEDI_TEST_ENVIRONMENT: 36 JEDI_TEST_ENVIRONMENT: 36
- TOXENV: py37
PYTHON_PATH: C:\Python37
JEDI_TEST_ENVIRONMENT: 35
- TOXENV: py37
PYTHON_PATH: C:\Python37
JEDI_TEST_ENVIRONMENT: 27
- TOXENV: py36 - TOXENV: py36
PYTHON_PATH: C:\Python36 PYTHON_PATH: C:\Python36
@@ -19,38 +13,6 @@ environment:
- TOXENV: py36 - TOXENV: py36
PYTHON_PATH: C:\Python36 PYTHON_PATH: C:\Python36
JEDI_TEST_ENVIRONMENT: 36 JEDI_TEST_ENVIRONMENT: 36
- TOXENV: py36
PYTHON_PATH: C:\Python36
JEDI_TEST_ENVIRONMENT: 35
- TOXENV: py36
PYTHON_PATH: C:\Python36
JEDI_TEST_ENVIRONMENT: 27
- TOXENV: py35
PYTHON_PATH: C:\Python35
JEDI_TEST_ENVIRONMENT: 37
- TOXENV: py35
PYTHON_PATH: C:\Python35
JEDI_TEST_ENVIRONMENT: 36
- TOXENV: py35
PYTHON_PATH: C:\Python35
JEDI_TEST_ENVIRONMENT: 35
- TOXENV: py35
PYTHON_PATH: C:\Python35
JEDI_TEST_ENVIRONMENT: 27
- TOXENV: py27
PYTHON_PATH: C:\Python27
JEDI_TEST_ENVIRONMENT: 37
- TOXENV: py27
PYTHON_PATH: C:\Python27
JEDI_TEST_ENVIRONMENT: 36
- TOXENV: py27
PYTHON_PATH: C:\Python27
JEDI_TEST_ENVIRONMENT: 35
- TOXENV: py27
PYTHON_PATH: C:\Python27
JEDI_TEST_ENVIRONMENT: 27
install: install:
- git submodule update --init --recursive - git submodule update --init --recursive
- set PATH=%PYTHON_PATH%;%PYTHON_PATH%\Scripts;%PATH% - set PATH=%PYTHON_PATH%;%PYTHON_PATH%\Scripts;%PATH%
+3 -41
View File
@@ -8,7 +8,6 @@ import pytest
import jedi import jedi
from jedi.api.environment import get_system_environment, InterpreterEnvironment from jedi.api.environment import get_system_environment, InterpreterEnvironment
from jedi._compatibility import py_version
from test.helpers import test_dir from test.helpers import test_dir
collect_ignore = [ collect_ignore = [
@@ -18,9 +17,6 @@ collect_ignore = [
'build/', 'build/',
'test/examples', 'test/examples',
] ]
if sys.version_info < (3, 6):
# Python 2 not supported syntax
collect_ignore.append('test/test_inference/test_mixed.py')
# The following hooks (pytest_configure, pytest_unconfigure) are used # The following hooks (pytest_configure, pytest_unconfigure) are used
@@ -45,7 +41,7 @@ def pytest_addoption(parser):
help="Warnings are treated as errors.") help="Warnings are treated as errors.")
parser.addoption("--env", action='store', parser.addoption("--env", action='store',
help="Execute the tests in that environment (e.g. 35 for python3.5).") help="Execute the tests in that environment (e.g. 39 for python3.9).")
parser.addoption("--interpreter-env", "-I", action='store_true', parser.addoption("--interpreter-env", "-I", action='store_true',
help="Don't use subprocesses to guarantee having safe " help="Don't use subprocesses to guarantee having safe "
"code execution. Useful for debugging.") "code execution. Useful for debugging.")
@@ -97,7 +93,8 @@ def clean_jedi_cache(request):
def environment(request): def environment(request):
version = request.config.option.env version = request.config.option.env
if version is None: if version is None:
version = os.environ.get('JEDI_TEST_ENVIRONMENT', str(py_version)) v = str(sys.version_info[0]) + str(sys.version_info[1])
version = os.environ.get('JEDI_TEST_ENVIRONMENT', v)
if request.config.option.interpreter_env or version == 'interpreter': if request.config.option.interpreter_env or version == 'interpreter':
return InterpreterEnvironment() return InterpreterEnvironment()
@@ -136,17 +133,6 @@ def goto_or_help_or_infer(request, Script):
return lambda code, *args, **kwargs: getattr(Script(code), request.param)(*args, **kwargs) return lambda code, *args, **kwargs: getattr(Script(code), request.param)(*args, **kwargs)
@pytest.fixture(scope='session')
def has_typing(environment):
if environment.version_info >= (3, 5, 0):
# This if is just needed to avoid that tests ever skip way more than
# they should for all Python versions.
return True
script = jedi.Script('import typing', environment=environment)
return bool(script.infer())
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def has_django(environment): def has_django(environment):
script = jedi.Script('import django', environment=environment) script = jedi.Script('import django', environment=environment)
@@ -158,14 +144,6 @@ def jedi_path():
return os.path.dirname(__file__) return os.path.dirname(__file__)
@pytest.fixture()
def skip_python2(environment):
if environment.version_info.major == 2:
# This if is just needed to avoid that tests ever skip way more than
# they should for all Python versions.
pytest.skip()
@pytest.fixture() @pytest.fixture()
def skip_pre_python38(environment): def skip_pre_python38(environment):
if environment.version_info < (3, 8): if environment.version_info < (3, 8):
@@ -180,19 +158,3 @@ def skip_pre_python37(environment):
# This if is just needed to avoid that tests ever skip way more than # This if is just needed to avoid that tests ever skip way more than
# they should for all Python versions. # they should for all Python versions.
pytest.skip() pytest.skip()
@pytest.fixture()
def skip_pre_python35(environment):
if environment.version_info < (3, 5):
# This if is just needed to avoid that tests ever skip way more than
# they should for all Python versions.
pytest.skip()
@pytest.fixture()
def skip_pre_python36(environment):
if environment.version_info < (3, 6):
# This if is just needed to avoid that tests ever skip way more than
# they should for all Python versions.
pytest.skip()
+8 -10
View File
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# Jedi documentation build configuration file, created by # Jedi documentation build configuration file, created by
# sphinx-quickstart on Wed Dec 26 00:11:34 2012. # sphinx-quickstart on Wed Dec 26 00:11:34 2012.
# #
@@ -43,8 +41,8 @@ source_encoding = 'utf-8'
master_doc = 'index' master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Jedi' project = 'Jedi'
copyright = u'jedi contributors' copyright = 'jedi contributors'
import jedi import jedi
from jedi.utils import version_info from jedi.utils import version_info
@@ -205,8 +203,8 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]). # (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [ latex_documents = [
('index', 'Jedi.tex', u'Jedi Documentation', ('index', 'Jedi.tex', 'Jedi Documentation',
u'Jedi contributors', 'manual'), 'Jedi contributors', 'manual'),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
@@ -235,8 +233,8 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [
('index', 'jedi', u'Jedi Documentation', ('index', 'jedi', 'Jedi Documentation',
[u'Jedi contributors'], 1) ['Jedi contributors'], 1)
] ]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
@@ -249,8 +247,8 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
('index', 'Jedi', u'Jedi Documentation', ('index', 'Jedi', 'Jedi Documentation',
u'Jedi contributors', 'Jedi', 'Awesome Python autocompletion library.', 'Jedi contributors', 'Jedi', 'Awesome Python autocompletion library.',
'Miscellaneous'), 'Miscellaneous'),
] ]
+1 -1
View File
@@ -107,7 +107,7 @@ Completions
>>> code = '''import json; json.l''' >>> code = '''import json; json.l'''
>>> script = jedi.Script(code, path='example.py') >>> script = jedi.Script(code, path='example.py')
>>> script >>> script
<Script: 'example.py' <SameEnvironment: 3.5.2 in /usr>> <Script: 'example.py' <SameEnvironment: 3.9.0 in /usr>>
>>> completions = script.complete(1, 19) >>> completions = script.complete(1, 19)
>>> completions >>> completions
[<Completion: load>, <Completion: loads>] [<Completion: load>, <Completion: loads>]
+1 -1
View File
@@ -16,7 +16,7 @@ Jedi's main API calls and features are:
Basic Features Basic Features
-------------- --------------
- Python 2.7 and 3.5+ support - Python 3.6+ 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
- Great ``virtualenv``/``venv`` support - Great ``virtualenv``/``venv`` support
-8
View File
@@ -50,14 +50,6 @@ Arch Linux
You can install |jedi| directly from official Arch Linux packages: You can install |jedi| directly from official Arch Linux packages:
- `python-jedi <https://www.archlinux.org/packages/community/any/python-jedi/>`__ - `python-jedi <https://www.archlinux.org/packages/community/any/python-jedi/>`__
(Python 3)
- `python2-jedi <https://www.archlinux.org/packages/community/any/python2-jedi/>`__
(Python 2)
The specified Python version just refers to the *runtime environment* for
|jedi|. Use the Python 2 version if you're running vim (or whatever editor you
use) under Python 2. Otherwise, use the Python 3 version. But whatever version
you choose, both are able to complete both Python 2 and 3 *code*.
(There is also a packaged version of the vim plugin available: (There is also a packaged version of the vim plugin available:
`vim-jedi at Arch Linux <https://www.archlinux.org/packages/community/any/vim-jedi/>`__.) `vim-jedi at Arch Linux <https://www.archlinux.org/packages/community/any/vim-jedi/>`__.)
+7 -590
View File
@@ -1,292 +1,13 @@
""" """
To ensure compatibility from Python ``2.7`` - ``3.x``, a module has been This module is here to ensure compatibility of Windows/Linux/MacOS and
created. Clearly there is huge need to use conforming syntax. different Python versions.
""" """
from __future__ import print_function
import atexit
import errno import errno
import functools
import sys import sys
import os import pickle
import re
import pkgutil
import warnings
import subprocess
import weakref
try:
import importlib
except ImportError:
pass
from zipimport import zipimporter
from jedi.file_io import KnownContentFileIO, ZipFileIO
is_py3 = sys.version_info[0] >= 3
is_py35 = is_py3 and sys.version_info[1] >= 5
py_version = int(str(sys.version_info[0]) + str(sys.version_info[1]))
if sys.version_info[:2] < (3, 5): def cast_path(string):
"""
A super-minimal shim around listdir that behave like
scandir for the information we need.
"""
class _DirEntry:
def __init__(self, name, basepath):
self.name = name
self.basepath = basepath
def is_dir(self):
path_for_name = os.path.join(self.basepath, self.name)
return os.path.isdir(path_for_name)
def scandir(dir):
return [_DirEntry(name, dir) for name in os.listdir(dir)]
else:
from os import scandir
class DummyFile(object):
def __init__(self, loader, string):
self.loader = loader
self.string = string
def read(self):
return self.loader.get_source(self.string)
def close(self):
del self.loader
def find_module_py34(string, path=None, full_name=None, is_global_search=True):
spec = None
loader = None
for finder in sys.meta_path:
if is_global_search and finder != importlib.machinery.PathFinder:
p = None
else:
p = path
try:
find_spec = finder.find_spec
except AttributeError:
# These are old-school clases that still have a different API, just
# ignore those.
continue
spec = find_spec(string, p)
if spec is not None:
loader = spec.loader
if loader is None and not spec.has_location:
# This is a namespace package.
full_name = string if not path else full_name
implicit_ns_info = ImplicitNSInfo(full_name, spec.submodule_search_locations._path)
return implicit_ns_info, True
break
return find_module_py33(string, path, loader)
def find_module_py33(string, path=None, loader=None, full_name=None, is_global_search=True):
loader = loader or importlib.machinery.PathFinder.find_module(string, path)
if loader is None and path is None: # Fallback to find builtins
try:
with warnings.catch_warnings(record=True):
# Mute "DeprecationWarning: Use importlib.util.find_spec()
# instead." While we should replace that in the future, it's
# probably good to wait until we deprecate Python 3.3, since
# it was added in Python 3.4 and find_loader hasn't been
# removed in 3.6.
loader = importlib.find_loader(string)
except ValueError as e:
# See #491. Importlib might raise a ValueError, to avoid this, we
# just raise an ImportError to fix the issue.
raise ImportError("Originally " + repr(e))
if loader is None:
raise ImportError("Couldn't find a loader for {}".format(string))
return _from_loader(loader, string)
def _from_loader(loader, string):
try:
is_package_method = loader.is_package
except AttributeError:
is_package = False
else:
is_package = is_package_method(string)
try:
get_filename = loader.get_filename
except AttributeError:
return None, is_package
else:
module_path = cast_path(get_filename(string))
# To avoid unicode and read bytes, "overwrite" loader.get_source if
# possible.
try:
f = type(loader).get_source
except AttributeError:
raise ImportError("get_source was not defined on loader")
if is_py3 and f is not importlib.machinery.SourceFileLoader.get_source:
# Unfortunately we are reading unicode here, not bytes.
# It seems hard to get bytes, because the zip importer
# logic just unpacks the zip file and returns a file descriptor
# that we cannot as easily access. Therefore we just read it as
# a string in the cases where get_source was overwritten.
code = loader.get_source(string)
else:
code = _get_source(loader, string)
if code is None:
return None, is_package
if isinstance(loader, zipimporter):
return ZipFileIO(module_path, code, cast_path(loader.archive)), is_package
return KnownContentFileIO(module_path, code), is_package
def _get_source(loader, fullname):
"""
This method is here as a replacement for SourceLoader.get_source. That
method returns unicode, but we prefer bytes.
"""
path = loader.get_filename(fullname)
try:
return loader.get_data(path)
except OSError:
raise ImportError('source not available through get_data()',
name=fullname)
def find_module_pre_py3(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
try:
module_file, module_path, description = imp.find_module(string, path)
module_type = description[2]
is_package = module_type is imp.PKG_DIRECTORY
if is_package:
# In Python 2 directory package imports are returned as folder
# paths, not __init__.py paths.
p = os.path.join(module_path, '__init__.py')
try:
module_file = open(p)
module_path = p
except FileNotFoundError:
pass
elif module_type != imp.PY_SOURCE:
if module_file is not None:
module_file.close()
module_file = None
if module_file is None:
return None, is_package
with module_file:
code = module_file.read()
return KnownContentFileIO(cast_path(module_path), code), is_package
except ImportError:
pass
if path is None:
path = sys.path
for item in path:
loader = pkgutil.get_importer(item)
if loader:
loader = loader.find_module(string)
if loader is not None:
return _from_loader(loader, string)
raise ImportError("No module named {}".format(string))
find_module = find_module_py34 if is_py3 else find_module_pre_py3
find_module.__doc__ = """
Provides information about a module.
This function isolates the differences in importing libraries introduced with
python 3.3 on; it gets a module name and optionally a path. It will return a
tuple containin an open file for the module (if not builtin), the filename
or the name of the module if it is a builtin one and a boolean indicating
if the module is contained in a package.
"""
class ImplicitNSInfo(object):
"""Stores information returned from an implicit namespace spec"""
def __init__(self, name, paths):
self.name = name
self.paths = paths
if is_py3:
all_suffixes = importlib.machinery.all_suffixes
else:
def all_suffixes():
# Is deprecated and raises a warning in Python 3.6.
import imp
return [suffix for suffix, _, _ in imp.get_suffixes()]
# unicode function
try:
unicode = unicode
except NameError:
unicode = str
# re-raise function
if is_py3:
def reraise(exception, traceback):
raise exception.with_traceback(traceback)
else:
eval(compile("""
def reraise(exception, traceback):
raise exception, None, traceback
""", 'blub', 'exec'))
reraise.__doc__ = """
Re-raise `exception` with a `traceback` object.
Usage::
reraise(Exception, sys.exc_info()[2])
"""
def use_metaclass(meta, *bases):
""" Create a class with a metaclass. """
if not bases:
bases = (object,)
return meta("Py2CompatibilityMetaClass", bases, {})
try:
encoding = sys.stdout.encoding
if encoding is None:
encoding = 'utf-8'
except AttributeError:
encoding = 'ascii'
def u(string, errors='strict'):
"""Cast to unicode DAMMIT!
Written because Python2 repr always implicitly casts to a string, so we
have to cast back to a unicode (and we now that we always deal with valid
unicode, because we check that in the beginning).
"""
if isinstance(string, bytes):
return unicode(string, encoding='UTF-8', errors=errors)
return string
def cast_path(obj):
""" """
Take a bytes or str path and cast it to unicode. Take a bytes or str path and cast it to unicode.
@@ -297,103 +18,13 @@ def cast_path(obj):
Since this just really complicates everything and Python 2.7 will be EOL Since this just really complicates everything and Python 2.7 will be EOL
soon anyway, just go with always strings. soon anyway, just go with always strings.
""" """
return u(obj, errors='replace') if isinstance(string, bytes):
return str(string, encoding='UTF-8', errors='replace')
return str(string)
def force_unicode(obj):
# Intentionally don't mix those two up, because those two code paths might
# be different in the future (maybe windows?).
return cast_path(obj)
try:
import builtins # module name in python 3
except ImportError:
import __builtin__ as builtins # noqa: F401
import ast # noqa: F401
def literal_eval(string):
return ast.literal_eval(string)
try:
from itertools import zip_longest
except ImportError:
from itertools import izip_longest as zip_longest # Python 2 # noqa: F401
try:
FileNotFoundError = FileNotFoundError
except NameError:
FileNotFoundError = IOError
try:
IsADirectoryError = IsADirectoryError
except NameError:
IsADirectoryError = IOError
try:
PermissionError = PermissionError
except NameError:
PermissionError = IOError
try:
NotADirectoryError = NotADirectoryError
except NameError:
class NotADirectoryError(Exception):
# Don't implement this for Python 2 anymore.
pass
def no_unicode_pprint(dct):
"""
Python 2/3 dict __repr__ may be different, because of unicode differens
(with or without a `u` prefix). Normally in doctests we could use `pprint`
to sort dicts and check for equality, but here we have to write a separate
function to do that.
"""
import pprint
s = pprint.pformat(dct)
print(re.sub("u'", "'", s))
def utf8_repr(func):
"""
``__repr__`` methods in Python 2 don't allow unicode objects to be
returned. Therefore cast them to utf-8 bytes in this decorator.
"""
def wrapper(self):
result = func(self)
if isinstance(result, unicode):
return result.encode('utf-8')
else:
return result
if is_py3:
return func
else:
return wrapper
if is_py3:
import queue
else:
import Queue as queue # noqa: F401
try:
# Attempt to load the C implementation of pickle on Python 2 as it is way
# faster.
import cPickle as pickle
except ImportError:
import pickle
def pickle_load(file): def pickle_load(file):
try: try:
if is_py3:
return pickle.load(file, encoding='bytes')
return pickle.load(file) return pickle.load(file)
# Python on Windows don't throw EOF errors for pipes. So reraise them with # Python on Windows don't throw EOF errors for pipes. So reraise them with
# the correct type, which is caught upwards. # the correct type, which is caught upwards.
@@ -403,24 +34,8 @@ def pickle_load(file):
raise raise
def _python2_dct_keys_to_unicode(data):
"""
Python 2 stores object __dict__ entries as bytes, not unicode, correct it
here. Python 2 can deal with both, Python 3 expects unicode.
"""
if isinstance(data, tuple):
return tuple(_python2_dct_keys_to_unicode(x) for x in data)
elif isinstance(data, list):
return list(_python2_dct_keys_to_unicode(x) for x in data)
elif hasattr(data, '__dict__') and type(data.__dict__) == dict:
data.__dict__ = {unicode(k): v for k, v in data.__dict__.items()}
return data
def pickle_dump(data, file, protocol): def pickle_dump(data, file, protocol):
try: try:
if not is_py3:
data = _python2_dct_keys_to_unicode(data)
pickle.dump(data, file, protocol) pickle.dump(data, file, protocol)
# On Python 3.3 flush throws sometimes an error even though the writing # On Python 3.3 flush throws sometimes an error even though the writing
# operation should be completed. # operation should be completed.
@@ -431,201 +46,3 @@ def pickle_dump(data, file, protocol):
if sys.platform == 'win32': if sys.platform == 'win32':
raise IOError(errno.EPIPE, "Broken pipe") raise IOError(errno.EPIPE, "Broken pipe")
raise raise
# Determine the highest protocol version compatible for a given list of Python
# versions.
def highest_pickle_protocol(python_versions):
protocol = 4
for version in python_versions:
if version[0] == 2:
# The minimum protocol version for the versions of Python that we
# support (2.7 and 3.3+) is 2.
return 2
if version[1] < 4:
protocol = 3
return protocol
try:
from inspect import Parameter
except ImportError:
class Parameter(object):
POSITIONAL_ONLY = object()
POSITIONAL_OR_KEYWORD = object()
VAR_POSITIONAL = object()
KEYWORD_ONLY = object()
VAR_KEYWORD = object()
class GeneralizedPopen(subprocess.Popen):
def __init__(self, *args, **kwargs):
if os.name == 'nt':
try:
# Was introduced in Python 3.7.
CREATE_NO_WINDOW = subprocess.CREATE_NO_WINDOW
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)
# shutil.which is not available on Python 2.7.
def which(cmd, mode=os.F_OK | os.X_OK, path=None):
"""Given a command, mode, and a PATH string, return the path which
conforms to the given mode on the PATH, or None if there is no such
file.
`mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
of os.environ.get("PATH"), or can be overridden with a custom search
path.
"""
# Check that a given file can be accessed with the correct mode.
# Additionally check that `file` is not a directory, as on Windows
# directories pass the os.access check.
def _access_check(fn, mode):
return (os.path.exists(fn) and os.access(fn, mode)
and not os.path.isdir(fn))
# If we're given a path with a directory part, look it up directly rather
# than referring to PATH directories. This includes checking relative to the
# current directory, e.g. ./script
if os.path.dirname(cmd):
if _access_check(cmd, mode):
return cmd
return None
if path is None:
path = os.environ.get("PATH", os.defpath)
if not path:
return None
path = path.split(os.pathsep)
if sys.platform == "win32":
# The current directory takes precedence on Windows.
if os.curdir not in path:
path.insert(0, os.curdir)
# PATHEXT is necessary to check on Windows.
pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
# See if the given file matches any of the expected path extensions.
# This will allow us to short circuit when given "python.exe".
# If it does match, only test that one, otherwise we have to try
# others.
if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
files = [cmd]
else:
files = [cmd + ext for ext in pathext]
else:
# On other platforms you don't have things like PATHEXT to tell you
# what file suffixes are executable, so just pass on cmd as-is.
files = [cmd]
seen = set()
for dir in path:
normdir = os.path.normcase(dir)
if normdir not in seen:
seen.add(normdir)
for thefile in files:
name = os.path.join(dir, thefile)
if _access_check(name, mode):
return name
return None
if not is_py3:
# Simplified backport of Python 3 weakref.finalize:
# https://github.com/python/cpython/blob/ded4737989316653469763230036b04513cb62b3/Lib/weakref.py#L502-L662
class finalize(object):
"""Class for finalization of weakrefable objects.
finalize(obj, func, *args, **kwargs) returns a callable finalizer
object which will be called when obj is garbage collected. The
first time the finalizer is called it evaluates func(*arg, **kwargs)
and returns the result. After this the finalizer is dead, and
calling it just returns None.
When the program exits any remaining finalizers will be run.
"""
# Finalizer objects don't have any state of their own.
# This ensures that they cannot be part of a ref-cycle.
__slots__ = ()
_registry = {}
def __init__(self, obj, func, *args, **kwargs):
info = functools.partial(func, *args, **kwargs)
info.weakref = weakref.ref(obj, self)
self._registry[self] = info
# To me it's an absolute mystery why in Python 2 we need _=None. It
# makes really no sense since it's never really called. Then again it
# might be called by Python 2.7 itself, but weakref.finalize is not
# documented in Python 2 and therefore shouldn't be randomly called.
# We never call this stuff with a parameter and therefore this
# parameter should not be needed. But it is. ~dave
def __call__(self, _=None):
"""Return func(*args, **kwargs) if alive."""
info = self._registry.pop(self, None)
if info:
return info()
@classmethod
def _exitfunc(cls):
if not cls._registry:
return
for finalizer in list(cls._registry):
try:
finalizer()
except Exception:
sys.excepthook(*sys.exc_info())
assert finalizer not in cls._registry
atexit.register(finalize._exitfunc)
weakref.finalize = finalize
if is_py3 and sys.version_info[1] > 5:
from inspect import unwrap
else:
# Only Python >=3.6 does properly limit the amount of unwraps. This is very
# relevant in the case of unittest.mock.patch.
# Below is the implementation of Python 3.7.
def unwrap(func, stop=None):
"""Get the object wrapped by *func*.
Follows the chain of :attr:`__wrapped__` attributes returning the last
object in the chain.
*stop* is an optional callback accepting an object in the wrapper chain
as its sole argument that allows the unwrapping to be terminated early if
the callback returns a true value. If the callback never returns a true
value, the last object in the chain is returned as usual. For example,
:func:`signature` uses this to stop unwrapping if any object in the
chain has a ``__signature__`` attribute defined.
:exc:`ValueError` is raised if a cycle is encountered.
"""
if stop is None:
def _is_wrapper(f):
return hasattr(f, '__wrapped__')
else:
def _is_wrapper(f):
return hasattr(f, '__wrapped__') and not stop(f)
f = func # remember the original func for error reporting
# Memoise by id to tolerate non-hashable objects, but store objects to
# ensure they aren't destroyed, which would allow their IDs to be reused.
memo = {id(f): f}
recursion_limit = sys.getrecursionlimit()
while _is_wrapper(func):
func = func.__wrapped__
id_func = id(func)
if (id_func in memo) or (len(memo) >= recursion_limit):
raise ValueError('wrapper loop when unwrapping {!r}'.format(f))
memo[id_func] = func
return func
+36 -93
View File
@@ -7,15 +7,14 @@ Alternatively, if you don't need a custom function and are happy with printing
debug messages to stdout, simply call :func:`set_debug_function` without debug messages to stdout, simply call :func:`set_debug_function` without
arguments. arguments.
""" """
import os
import sys import sys
import warnings import warnings
from functools import wraps from pathlib import Path
import parso import parso
from parso.python import tree from parso.python import tree
from jedi._compatibility import force_unicode, cast_path, is_py3 from jedi._compatibility import cast_path
from jedi.parser_utils import get_executable_nodes from jedi.parser_utils import get_executable_nodes
from jedi import debug from jedi import debug
from jedi import settings from jedi import settings
@@ -51,18 +50,6 @@ from jedi.inference.utils import to_list
sys.setrecursionlimit(3000) sys.setrecursionlimit(3000)
def _no_python2_support(func):
# TODO remove when removing Python 2/3.5
@wraps(func)
def wrapper(self, *args, **kwargs):
if self._inference_state.grammar.version_info < (3, 6) or sys.version_info < (3, 6):
raise NotImplementedError(
"No support for refactorings/search on Python 2/3.5"
)
return func(self, *args, **kwargs)
return wrapper
class Script(object): class Script(object):
""" """
A Script is the base for completions, goto or whatever you want to do with A Script is the base for completions, goto or whatever you want to do with
@@ -109,10 +96,7 @@ class Script(object):
:type column: int :type column: int
:param path: The path of the file in the file system, or ``''`` if :param path: The path of the file in the file system, or ``''`` if
it hasn't been saved yet. it hasn't been saved yet.
:type path: str or None :type path: str or pathlib.Path or None
:param encoding: Deprecated, cast to unicode yourself. The encoding of
``code``, if it is not a ``unicode`` object (default ``'utf-8'``).
:type encoding: str
:param sys_path: Deprecated, use the project parameter. :param sys_path: Deprecated, use the project parameter.
:type sys_path: typing.List[str] :type sys_path: typing.List[str]
:param Environment environment: Provide a predefined :ref:`Environment <environments>` :param Environment environment: Provide a predefined :ref:`Environment <environments>`
@@ -122,21 +106,14 @@ class Script(object):
also ways to modify the sys path and other things. also ways to modify the sys path and other things.
""" """
def __init__(self, code=None, line=None, column=None, path=None, def __init__(self, code=None, line=None, column=None, path=None,
encoding=None, sys_path=None, environment=None, sys_path=None, environment=None, project=None, source=None):
project=None, source=None):
self._orig_path = path self._orig_path = path
# An empty path (also empty string) should always result in no path. # An empty path (also empty string) should always result in no path.
self.path = os.path.abspath(path) if path else None if isinstance(path, str):
path = Path(path)
self.path = path.absolute() if path else None
if encoding is None:
encoding = 'utf-8'
else:
warnings.warn(
"Deprecated since version 0.17.0. You should cast to valid "
"unicode yourself, especially if you are not using utf-8.",
DeprecationWarning,
stacklevel=2
)
if line is not None: if line is not None:
warnings.warn( warnings.warn(
"Providing the line is now done in the functions themselves " "Providing the line is now done in the functions themselves "
@@ -163,14 +140,9 @@ class Script(object):
with open(path, 'rb') as f: with open(path, 'rb') as f:
code = f.read() code = f.read()
if sys_path is not None and not is_py3:
sys_path = list(map(force_unicode, sys_path))
if project is None: if project is None:
# Load the Python grammar of the current interpreter. # Load the Python grammar of the current interpreter.
project = get_default_project( project = get_default_project(None if self.path is None else self.path.parent)
os.path.dirname(self.path) if path else None
)
# TODO deprecate and remove sys_path from the Script API. # TODO deprecate and remove sys_path from the Script API.
if sys_path is not None: if sys_path is not None:
project._sys_path = sys_path project._sys_path = sys_path
@@ -188,8 +160,7 @@ class Script(object):
self._module_node, code = self._inference_state.parse_and_get_code( self._module_node, code = self._inference_state.parse_and_get_code(
code=code, code=code,
path=self.path, path=self.path,
encoding=encoding, use_latest_grammar=path and path.suffix == 'pyi',
use_latest_grammar=path and path.endswith('.pyi'),
cache=False, # No disk cache, because the current script often changes. cache=False, # No disk cache, because the current script often changes.
diff_cache=settings.fast_parser, diff_cache=settings.fast_parser,
cache_path=settings.cache_directory, cache_path=settings.cache_directory,
@@ -221,7 +192,7 @@ class Script(object):
file_io = None file_io = None
else: else:
file_io = KnownContentFileIO(cast_path(self.path), self._code) file_io = KnownContentFileIO(cast_path(self.path), self._code)
if self.path is not None and self.path.endswith('.pyi'): if self.path is not None and self.path.suffix == '.pyi':
# We are in a stub file. Try to load the stub properly. # We are in a stub file. Try to load the stub properly.
stub_module = load_proper_stub_module( stub_module = load_proper_stub_module(
self._inference_state, self._inference_state,
@@ -242,7 +213,7 @@ class Script(object):
code_lines=self._code_lines, code_lines=self._code_lines,
is_package=is_package, is_package=is_package,
) )
if names[0] not in ('builtins', '__builtin__', 'typing'): if names[0] not in ('builtins', 'typing'):
# These modules are essential for Jedi, so don't overwrite them. # These modules are essential for Jedi, so don't overwrite them.
self._inference_state.module_cache.add(names, ValueSet([module])) self._inference_state.module_cache.add(names, ValueSet([module]))
return module return module
@@ -258,7 +229,7 @@ class Script(object):
) )
@validate_line_column @validate_line_column
def complete(self, line=None, column=None, **kwargs): def complete(self, line=None, column=None, *, fuzzy=False):
""" """
Completes objects under the cursor. Completes objects under the cursor.
@@ -272,9 +243,6 @@ class Script(object):
before magic methods and name mangled names that start with ``__``. before magic methods and name mangled names that start with ``__``.
:rtype: list of :class:`.Completion` :rtype: list of :class:`.Completion`
""" """
return self._complete(line, column, **kwargs)
def _complete(self, line, column, fuzzy=False): # Python 2...
with debug.increase_indent_cm('complete'): with debug.increase_indent_cm('complete'):
completion = Completion( completion = Completion(
self._inference_state, self._get_module_context(), self._code_lines, self._inference_state, self._get_module_context(), self._code_lines,
@@ -291,7 +259,7 @@ class Script(object):
return self.complete(*self._pos, fuzzy=fuzzy) return self.complete(*self._pos, fuzzy=fuzzy)
@validate_line_column @validate_line_column
def infer(self, line=None, column=None, **kwargs): def infer(self, line=None, column=None, *, only_stubs=False, prefer_stubs=False):
""" """
Return the definitions of under the cursor. It is basically a wrapper Return the definitions of under the cursor. It is basically a wrapper
around Jedi's type inference. around Jedi's type inference.
@@ -307,18 +275,6 @@ class Script(object):
:param prefer_stubs: Prefer stubs to Python objects for this method. :param prefer_stubs: Prefer stubs to Python objects for this method.
:rtype: list of :class:`.Name` :rtype: list of :class:`.Name`
""" """
with debug.increase_indent_cm('infer'):
return self._infer(line, column, **kwargs)
def goto_definitions(self, **kwargs):
warnings.warn(
"Deprecated since version 0.16.0. Use Script(...).infer instead.",
DeprecationWarning,
stacklevel=2
)
return self.infer(*self._pos, **kwargs)
def _infer(self, line, column, only_stubs=False, prefer_stubs=False):
pos = line, column pos = line, column
leaf = self._module_node.get_name_of_position(pos) leaf = self._module_node.get_name_of_position(pos)
if leaf is None: if leaf is None:
@@ -341,6 +297,14 @@ class Script(object):
# the API. # the API.
return helpers.sorted_definitions(set(defs)) return helpers.sorted_definitions(set(defs))
def goto_definitions(self, **kwargs):
warnings.warn(
"Deprecated since version 0.16.0. Use Script(...).infer instead.",
DeprecationWarning,
stacklevel=2
)
return self.infer(*self._pos, **kwargs)
def goto_assignments(self, follow_imports=False, follow_builtin_imports=False, **kwargs): def goto_assignments(self, follow_imports=False, follow_builtin_imports=False, **kwargs):
warnings.warn( warnings.warn(
"Deprecated since version 0.16.0. Use Script(...).goto instead.", "Deprecated since version 0.16.0. Use Script(...).goto instead.",
@@ -353,7 +317,8 @@ class Script(object):
**kwargs) **kwargs)
@validate_line_column @validate_line_column
def goto(self, line=None, column=None, **kwargs): def goto(self, line=None, column=None, *, follow_imports=False, follow_builtin_imports=False,
only_stubs=False, prefer_stubs=False):
""" """
Goes to the name that defined the object under the cursor. Optionally Goes to the name that defined the object under the cursor. Optionally
you can follow imports. you can follow imports.
@@ -367,11 +332,6 @@ class Script(object):
:param prefer_stubs: Prefer stubs to Python objects for this method. :param prefer_stubs: Prefer stubs to Python objects for this method.
:rtype: list of :class:`.Name` :rtype: list of :class:`.Name`
""" """
with debug.increase_indent_cm('goto'):
return self._goto(line, column, **kwargs)
def _goto(self, line, column, follow_imports=False, follow_builtin_imports=False,
only_stubs=False, prefer_stubs=False):
tree_name = self._module_node.get_name_of_position((line, column)) tree_name = self._module_node.get_name_of_position((line, column))
if tree_name is None: if tree_name is None:
# Without a name we really just want to jump to the result e.g. # Without a name we really just want to jump to the result e.g.
@@ -407,8 +367,7 @@ class Script(object):
# Avoid duplicates # Avoid duplicates
return list(set(helpers.sorted_definitions(defs))) return list(set(helpers.sorted_definitions(defs)))
@_no_python2_support def search(self, string, *, all_scopes=False):
def search(self, string, **kwargs):
""" """
Searches a name in the current file. For a description of how the Searches a name in the current file. For a description of how the
search string should look like, please have a look at search string should look like, please have a look at
@@ -419,9 +378,6 @@ class Script(object):
functions and classes. functions and classes.
:yields: :class:`.Name` :yields: :class:`.Name`
""" """
return self._search(string, **kwargs) # Python 2 ...
def _search(self, string, all_scopes=False):
return self._search_func(string, all_scopes=all_scopes) return self._search_func(string, all_scopes=all_scopes)
@to_list @to_list
@@ -685,8 +641,7 @@ class Script(object):
] ]
return sorted(defs, key=lambda x: x.start_pos) return sorted(defs, key=lambda x: x.start_pos)
@_no_python2_support def rename(self, line=None, column=None, *, new_name):
def rename(self, line=None, column=None, **kwargs):
""" """
Renames all references of the variable under the cursor. Renames all references of the variable under the cursor.
@@ -695,14 +650,11 @@ class Script(object):
:raises: :exc:`.RefactoringError` :raises: :exc:`.RefactoringError`
:rtype: :class:`.Refactoring` :rtype: :class:`.Refactoring`
""" """
return self._rename(line, column, **kwargs)
def _rename(self, line, column, new_name): # Python 2...
definitions = self.get_references(line, column, include_builtins=False) definitions = self.get_references(line, column, include_builtins=False)
return refactoring.rename(self._inference_state, definitions, new_name) return refactoring.rename(self._inference_state, definitions, new_name)
@_no_python2_support @validate_line_column
def extract_variable(self, line, column, **kwargs): def extract_variable(self, line, column, *, new_name, until_line=None, until_column=None):
""" """
Moves an expression to a new statemenet. Moves an expression to a new statemenet.
@@ -727,10 +679,6 @@ class Script(object):
:raises: :exc:`.RefactoringError` :raises: :exc:`.RefactoringError`
:rtype: :class:`.Refactoring` :rtype: :class:`.Refactoring`
""" """
return self._extract_variable(line, column, **kwargs) # Python 2...
@validate_line_column
def _extract_variable(self, line, column, new_name, until_line=None, until_column=None):
if until_line is None and until_column is None: if until_line is None and until_column is None:
until_pos = None until_pos = None
else: else:
@@ -744,8 +692,8 @@ class Script(object):
new_name, (line, column), until_pos new_name, (line, column), until_pos
) )
@_no_python2_support @validate_line_column
def extract_function(self, line, column, **kwargs): def extract_function(self, line, column, *, new_name, until_line=None, until_column=None):
""" """
Moves an expression to a new function. Moves an expression to a new function.
@@ -778,10 +726,6 @@ class Script(object):
:raises: :exc:`.RefactoringError` :raises: :exc:`.RefactoringError`
:rtype: :class:`.Refactoring` :rtype: :class:`.Refactoring`
""" """
return self._extract_function(line, column, **kwargs) # Python 2...
@validate_line_column
def _extract_function(self, line, column, new_name, until_line=None, until_column=None):
if until_line is None and until_column is None: if until_line is None and until_column is None:
until_pos = None until_pos = None
else: else:
@@ -795,7 +739,6 @@ class Script(object):
new_name, (line, column), until_pos new_name, (line, column), until_pos
) )
@_no_python2_support
def inline(self, line=None, column=None): def inline(self, line=None, column=None):
""" """
Inlines a variable under the cursor. This is basically the opposite of Inlines a variable under the cursor. This is basically the opposite of
@@ -855,8 +798,8 @@ class Interpreter(Script):
if not isinstance(environment, InterpreterEnvironment): if not isinstance(environment, InterpreterEnvironment):
raise TypeError("The environment needs to be an InterpreterEnvironment subclass.") raise TypeError("The environment needs to be an InterpreterEnvironment subclass.")
super(Interpreter, self).__init__(code, environment=environment, super().__init__(code, environment=environment,
project=Project(os.getcwd()), **kwds) project=Project(Path.cwd()), **kwds)
self.namespaces = namespaces self.namespaces = namespaces
self._inference_state.allow_descriptor_getattr = self._allow_descriptor_getattr_default self._inference_state.allow_descriptor_getattr = self._allow_descriptor_getattr_default
@@ -864,7 +807,7 @@ class Interpreter(Script):
def _get_module_context(self): def _get_module_context(self):
tree_module_value = ModuleValue( tree_module_value = ModuleValue(
self._inference_state, self._module_node, self._inference_state, self._module_node,
file_io=KnownContentFileIO(self.path, self._code), file_io=KnownContentFileIO(str(self.path), self._code),
string_names=('__main__',), string_names=('__main__',),
code_lines=self._code_lines, code_lines=self._code_lines,
) )
@@ -874,7 +817,7 @@ class Interpreter(Script):
) )
def names(source=None, path=None, encoding='utf-8', all_scopes=False, def names(source=None, path=None, all_scopes=False,
definitions=True, references=False, environment=None): definitions=True, references=False, environment=None):
warnings.warn( warnings.warn(
"Deprecated since version 0.16.0. Use Script(...).get_names instead.", "Deprecated since version 0.16.0. Use Script(...).get_names instead.",
@@ -882,7 +825,7 @@ def names(source=None, path=None, encoding='utf-8', all_scopes=False,
stacklevel=2 stacklevel=2
) )
return Script(source, path=path, encoding=encoding).get_names( return Script(source, path=path).get_names(
all_scopes=all_scopes, all_scopes=all_scopes,
definitions=definitions, definitions=definitions,
references=references, references=references,
@@ -899,7 +842,7 @@ def preload_module(*modules):
""" """
for m in modules: for m in modules:
s = "import %s as x; x." % m s = "import %s as x; x." % m
Script(s, path=None).complete(1, len(s)) Script(s).complete(1, len(s))
def set_debug_function(func_cb=debug.print_to_stdout, warnings=True, def set_debug_function(func_cb=debug.print_to_stdout, warnings=True,
+35 -46
View File
@@ -14,8 +14,8 @@ These classes are the much biggest part of the API, because they contain
the interesting information about all operations. the interesting information about all operations.
""" """
import re import re
import sys
import warnings import warnings
from typing import Optional
from parso.python.tree import search_ancestor from parso.python.tree import search_ancestor
@@ -71,7 +71,6 @@ class BaseName(object):
'_collections': 'collections', '_collections': 'collections',
'_socket': 'socket', '_socket': 'socket',
'_sqlite3': 'sqlite3', '_sqlite3': 'sqlite3',
'__builtin__': 'builtins',
} }
_tuple_mapping = dict((tuple(k.split('.')), v) for (k, v) in { _tuple_mapping = dict((tuple(k.split('.')), v) for (k, v) in {
@@ -94,9 +93,9 @@ class BaseName(object):
return self._name.get_root_context() return self._name.get_root_context()
@property @property
def module_path(self): def module_path(self) -> Optional[str]:
""" """
Shows the file path of a module. e.g. ``/usr/lib/python2.7/os.py`` Shows the file path of a module. e.g. ``/usr/lib/python3.9/os.py``
:rtype: str or None :rtype: str or None
""" """
@@ -104,7 +103,9 @@ class BaseName(object):
if module.is_stub() or not module.is_compiled(): if module.is_stub() or not module.is_compiled():
# Compiled modules should not return a module path even if they # Compiled modules should not return a module path even if they
# have one. # have one.
return self._get_module_context().py__file__() path = self._get_module_context().py__file__()
if path is not None:
return path
return None return None
@@ -129,7 +130,6 @@ class BaseName(object):
to Jedi, :meth:`jedi.Script.infer` should return a list of to Jedi, :meth:`jedi.Script.infer` should return a list of
definition for ``sys``, ``f``, ``C`` and ``x``. definition for ``sys``, ``f``, ``C`` and ``x``.
>>> from jedi._compatibility import no_unicode_pprint
>>> from jedi import Script >>> from jedi import Script
>>> source = ''' >>> source = '''
... import keyword ... import keyword
@@ -155,7 +155,7 @@ class BaseName(object):
so that it is easy to relate the result to the source code. so that it is easy to relate the result to the source code.
>>> defs = sorted(defs, key=lambda d: d.line) >>> defs = sorted(defs, key=lambda d: d.line)
>>> no_unicode_pprint(defs) # doctest: +NORMALIZE_WHITESPACE >>> print(defs) # doctest: +NORMALIZE_WHITESPACE
[<Name full_name='keyword', description='module keyword'>, [<Name full_name='keyword', description='module keyword'>,
<Name full_name='__main__.C', description='class C'>, <Name full_name='__main__.C', description='class C'>,
<Name full_name='__main__.D', description='instance D'>, <Name full_name='__main__.D', description='instance D'>,
@@ -163,7 +163,7 @@ class BaseName(object):
Finally, here is what you can get from :attr:`type`: Finally, here is what you can get from :attr:`type`:
>>> defs = [str(d.type) for d in defs] # It's unicode and in Py2 has u before it. >>> defs = [d.type for d in defs]
>>> defs[0] >>> defs[0]
'module' 'module'
>>> defs[1] >>> defs[1]
@@ -324,7 +324,6 @@ class BaseName(object):
Example: Example:
>>> from jedi._compatibility import no_unicode_pprint
>>> from jedi import Script >>> from jedi import Script
>>> source = ''' >>> source = '''
... def f(): ... def f():
@@ -337,10 +336,10 @@ class BaseName(object):
>>> script = Script(source) # line is maximum by default >>> script = Script(source) # line is maximum by default
>>> defs = script.infer(column=3) >>> defs = script.infer(column=3)
>>> defs = sorted(defs, key=lambda d: d.line) >>> defs = sorted(defs, key=lambda d: d.line)
>>> no_unicode_pprint(defs) # doctest: +NORMALIZE_WHITESPACE >>> print(defs) # doctest: +NORMALIZE_WHITESPACE
[<Name full_name='__main__.f', description='def f'>, [<Name full_name='__main__.f', description='def f'>,
<Name full_name='__main__.C', description='class C'>] <Name full_name='__main__.C', description='class C'>]
>>> str(defs[0].description) # strip literals in python2 >>> str(defs[0].description)
'def f' 'def f'
>>> str(defs[1].description) >>> str(defs[1].description)
'class C' 'class C'
@@ -424,7 +423,10 @@ class BaseName(object):
return False return False
return tree_name.is_definition() and tree_name.parent.type == 'trailer' return tree_name.is_definition() and tree_name.parent.type == 'trailer'
def goto(self, **kwargs): @debug.increase_indent_cm('goto on name')
def goto(self, *, follow_imports=False, follow_builtin_imports=False,
only_stubs=False, prefer_stubs=False):
""" """
Like :meth:`.Script.goto` (also supports the same params), but does it Like :meth:`.Script.goto` (also supports the same params), but does it
for the current name. This is typically useful if you are using for the current name. This is typically useful if you are using
@@ -437,20 +439,6 @@ class BaseName(object):
:param prefer_stubs: Prefer stubs to Python objects for this goto call. :param prefer_stubs: Prefer stubs to Python objects for this goto call.
:rtype: list of :class:`Name` :rtype: list of :class:`Name`
""" """
with debug.increase_indent_cm('goto for %s' % self._name):
return self._goto(**kwargs)
def goto_assignments(self, **kwargs): # Python 2...
warnings.warn(
"Deprecated since version 0.16.0. Use .goto.",
DeprecationWarning,
stacklevel=2
)
return self.goto(**kwargs)
def _goto(self, follow_imports=False, follow_builtin_imports=False,
only_stubs=False, prefer_stubs=False):
if not self._name.is_value_name: if not self._name.is_value_name:
return [] return []
@@ -465,7 +453,16 @@ class BaseName(object):
return [self if n == self._name else Name(self._inference_state, n) return [self if n == self._name else Name(self._inference_state, n)
for n in names] for n in names]
def infer(self, **kwargs): # Python 2... def goto_assignments(self, **kwargs):
warnings.warn(
"Deprecated since version 0.16.0. Use .goto.",
DeprecationWarning,
stacklevel=2
)
return self.goto(**kwargs)
@debug.increase_indent_cm('infer on name')
def infer(self, *, only_stubs=False, prefer_stubs=False):
""" """
Like :meth:`.Script.infer`, it can be useful to understand which type Like :meth:`.Script.infer`, it can be useful to understand which type
the current name has. the current name has.
@@ -482,10 +479,6 @@ class BaseName(object):
inference call. inference call.
:rtype: list of :class:`Name` :rtype: list of :class:`Name`
""" """
with debug.increase_indent_cm('infer for %s' % self._name):
return self._infer(**kwargs)
def _infer(self, only_stubs=False, prefer_stubs=False):
assert not (only_stubs and prefer_stubs) assert not (only_stubs and prefer_stubs)
if not self._name.is_value_name: if not self._name.is_value_name:
@@ -645,7 +638,7 @@ class Completion(BaseName):
""" """
def __init__(self, inference_state, name, stack, like_name_length, def __init__(self, inference_state, name, stack, like_name_length,
is_fuzzy, cached_name=None): is_fuzzy, cached_name=None):
super(Completion, self).__init__(inference_state, name) super().__init__(inference_state, name)
self._like_name_length = like_name_length self._like_name_length = like_name_length
self._stack = stack self._stack = stack
@@ -716,7 +709,7 @@ class Completion(BaseName):
# wouldn't load like > 100 Python modules anymore. # wouldn't load like > 100 Python modules anymore.
fast = False fast = False
return super(Completion, self).docstring(raw=raw, fast=fast) return super().docstring(raw=raw, fast=fast)
def _get_docstring(self): def _get_docstring(self):
if self._cached_name is not None: if self._cached_name is not None:
@@ -725,7 +718,7 @@ class Completion(BaseName):
self._name.get_public_name(), self._name.get_public_name(),
lambda: self._get_cache() lambda: self._get_cache()
) )
return super(Completion, self)._get_docstring() return super()._get_docstring()
def _get_docstring_signature(self): def _get_docstring_signature(self):
if self._cached_name is not None: if self._cached_name is not None:
@@ -734,13 +727,13 @@ class Completion(BaseName):
self._name.get_public_name(), self._name.get_public_name(),
lambda: self._get_cache() lambda: self._get_cache()
) )
return super(Completion, self)._get_docstring_signature() return super()._get_docstring_signature()
def _get_cache(self): def _get_cache(self):
return ( return (
super(Completion, self).type, super().type,
super(Completion, self)._get_docstring_signature(), super()._get_docstring_signature(),
super(Completion, self)._get_docstring(), super()._get_docstring(),
) )
@property @property
@@ -756,7 +749,7 @@ class Completion(BaseName):
lambda: self._get_cache() lambda: self._get_cache()
) )
return super(Completion, self).type return super().type
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self._name.get_public_name()) return '<%s: %s>' % (type(self).__name__, self._name.get_public_name())
@@ -768,7 +761,7 @@ class Name(BaseName):
:meth:`.Script.goto` or :meth:`.Script.infer`. :meth:`.Script.goto` or :meth:`.Script.infer`.
""" """
def __init__(self, inference_state, definition): def __init__(self, inference_state, definition):
super(Name, self).__init__(inference_state, definition) super().__init__(inference_state, definition)
@property @property
def desc_with_module(self): def desc_with_module(self):
@@ -821,7 +814,7 @@ class BaseSignature(Name):
calls. calls.
""" """
def __init__(self, inference_state, signature): def __init__(self, inference_state, signature):
super(BaseSignature, self).__init__(inference_state, signature.name) super().__init__(inference_state, signature.name)
self._signature = signature self._signature = signature
@property @property
@@ -851,7 +844,7 @@ class Signature(BaseSignature):
:meth:`.Script.get_signatures`. :meth:`.Script.get_signatures`.
""" """
def __init__(self, inference_state, signature, call_details): def __init__(self, inference_state, signature, call_details):
super(Signature, self).__init__(inference_state, signature) super().__init__(inference_state, signature)
self._call_details = call_details self._call_details = call_details
self._signature = signature self._signature = signature
@@ -918,8 +911,4 @@ class ParamName(Name):
:rtype: :py:attr:`inspect.Parameter.kind` :rtype: :py:attr:`inspect.Parameter.kind`
""" """
if sys.version_info < (3, 5):
raise NotImplementedError(
'Python 2 is end-of-life, the new feature is not available for it'
)
return self._name.get_kind() return self._name.get_kind()
+5 -8
View File
@@ -1,12 +1,12 @@
import re import re
from textwrap import dedent from textwrap import dedent
from inspect import Parameter
from parso.python.token import PythonTokenTypes from parso.python.token import PythonTokenTypes
from parso.python import tree from parso.python import tree
from parso.tree import search_ancestor, Leaf from parso.tree import search_ancestor, Leaf
from parso import split_lines from parso import split_lines
from jedi._compatibility import Parameter
from jedi import debug from jedi import debug
from jedi import settings from jedi import settings
from jedi.api import classes from jedi.api import classes
@@ -34,9 +34,7 @@ def _get_signature_param_names(signatures, positional_count, used_kwargs):
# Add named params # Add named params
for call_sig in signatures: for call_sig in signatures:
for i, p in enumerate(call_sig.params): for i, p in enumerate(call_sig.params):
# Allow protected access, because it's a public API. kind = p.kind
# TODO reconsider with Python 2 drop
kind = p._name.get_kind()
if i < positional_count and kind == Parameter.POSITIONAL_OR_KEYWORD: if i < positional_count and kind == Parameter.POSITIONAL_OR_KEYWORD:
continue continue
if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) \ if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) \
@@ -51,8 +49,7 @@ def _must_be_kwarg(signatures, positional_count, used_kwargs):
must_be_kwarg = True must_be_kwarg = True
for signature in signatures: for signature in signatures:
for i, p in enumerate(signature.params): for i, p in enumerate(signature.params):
# TODO reconsider with Python 2 drop kind = p.kind
kind = p._name.get_kind()
if kind is Parameter.VAR_POSITIONAL: if kind is Parameter.VAR_POSITIONAL:
# In case there were not already kwargs, the next param can # In case there were not already kwargs, the next param can
# always be a normal argument. # always be a normal argument.
@@ -579,8 +576,8 @@ def _complete_getattr(user_context, instance):
will write it like this anyway and the other ones, well they are just will write it like this anyway and the other ones, well they are just
out of luck I guess :) ~dave. out of luck I guess :) ~dave.
""" """
names = (instance.get_function_slot_names(u'__getattr__') names = (instance.get_function_slot_names('__getattr__')
or instance.get_function_slot_names(u'__getattribute__')) or instance.get_function_slot_names('__getattribute__'))
functions = ValueSet.from_sets( functions = ValueSet.from_sets(
name.infer() name.infer()
for name in names for name in names
+38 -61
View File
@@ -7,8 +7,8 @@ import sys
import hashlib import hashlib
import filecmp import filecmp
from collections import namedtuple from collections import namedtuple
from shutil import which
from jedi._compatibility import highest_pickle_protocol, which
from jedi.cache import memoize_method, time_cache from jedi.cache import memoize_method, time_cache
from jedi.inference.compiled.subprocess import CompiledSubprocess, \ from jedi.inference.compiled.subprocess import CompiledSubprocess, \
InferenceStateSameProcess, InferenceStateSubprocess InferenceStateSameProcess, InferenceStateSubprocess
@@ -17,7 +17,7 @@ import parso
_VersionInfo = namedtuple('VersionInfo', 'major minor micro') _VersionInfo = namedtuple('VersionInfo', 'major minor micro')
_SUPPORTED_PYTHONS = ['3.8', '3.7', '3.6', '3.5', '2.7'] _SUPPORTED_PYTHONS = ['3.9', '3.8', '3.7', '3.6']
_SAFE_PATHS = ['/usr/bin', '/usr/local/bin'] _SAFE_PATHS = ['/usr/bin', '/usr/local/bin']
_CONDA_VAR = 'CONDA_PREFIX' _CONDA_VAR = 'CONDA_PREFIX'
_CURRENT_VERSION = '%s.%s' % (sys.version_info.major, sys.version_info.minor) _CURRENT_VERSION = '%s.%s' % (sys.version_info.major, sys.version_info.minor)
@@ -96,16 +96,6 @@ class Environment(_BaseEnvironment):
Like :data:`sys.version_info`: a tuple to show the current Like :data:`sys.version_info`: a tuple to show the current
Environment's Python version. Environment's Python version.
""" """
# py2 sends bytes via pickle apparently?!
if self.version_info.major == 2:
self.executable = self.executable.decode()
self.path = self.path.decode()
# Adjust pickle protocol according to host and client version.
self._subprocess._pickle_protocol = highest_pickle_protocol([
sys.version_info, self.version_info])
return self._subprocess return self._subprocess
def __repr__(self): def __repr__(self):
@@ -267,7 +257,7 @@ def _get_cached_default_environment():
return InterpreterEnvironment() return InterpreterEnvironment()
def find_virtualenvs(paths=None, **kwargs): def find_virtualenvs(paths=None, *, safe=True, use_environment_vars=True):
""" """
:param paths: A list of paths in your file system to be scanned for :param paths: A list of paths in your file system to be scanned for
Virtualenvs. It will search in these paths and potentially execute the Virtualenvs. It will search in these paths and potentially execute the
@@ -284,47 +274,44 @@ def find_virtualenvs(paths=None, **kwargs):
:yields: :class:`.Environment` :yields: :class:`.Environment`
""" """
def py27_comp(paths=None, safe=True, use_environment_vars=True): if paths is None:
if paths is None: paths = []
paths = []
_used_paths = set() _used_paths = set()
if use_environment_vars: if use_environment_vars:
# Using this variable should be safe, because attackers might be # Using this variable should be safe, because attackers might be
# able to drop files (via git) but not environment variables. # able to drop files (via git) but not environment variables.
virtual_env = _get_virtual_env_from_var() virtual_env = _get_virtual_env_from_var()
if virtual_env is not None: if virtual_env is not None:
yield virtual_env yield virtual_env
_used_paths.add(virtual_env.path) _used_paths.add(virtual_env.path)
conda_env = _get_virtual_env_from_var(_CONDA_VAR) conda_env = _get_virtual_env_from_var(_CONDA_VAR)
if conda_env is not None: if conda_env is not None:
yield conda_env yield conda_env
_used_paths.add(conda_env.path) _used_paths.add(conda_env.path)
for directory in paths: for directory in paths:
if not os.path.isdir(directory): if not os.path.isdir(directory):
continue
directory = os.path.abspath(directory)
for path in os.listdir(directory):
path = os.path.join(directory, path)
if path in _used_paths:
# A path shouldn't be inferred twice.
continue continue
_used_paths.add(path)
directory = os.path.abspath(directory) try:
for path in os.listdir(directory): executable = _get_executable_path(path, safe=safe)
path = os.path.join(directory, path) yield Environment(executable)
if path in _used_paths: except InvalidPythonEnvironment:
# A path shouldn't be inferred twice. pass
continue
_used_paths.add(path)
try:
executable = _get_executable_path(path, safe=safe)
yield Environment(executable)
except InvalidPythonEnvironment:
pass
return py27_comp(paths, **kwargs)
def find_system_environments(**kwargs): def find_system_environments(*, env_vars={}):
""" """
Ignores virtualenvs and returns the Python versions that were installed on Ignores virtualenvs and returns the Python versions that were installed on
your system. This might return nothing, if you're running Python e.g. from your system. This might return nothing, if you're running Python e.g. from
@@ -336,14 +323,14 @@ def find_system_environments(**kwargs):
""" """
for version_string in _SUPPORTED_PYTHONS: for version_string in _SUPPORTED_PYTHONS:
try: try:
yield get_system_environment(version_string, **kwargs) yield get_system_environment(version_string, env_vars=env_vars)
except InvalidPythonEnvironment: except InvalidPythonEnvironment:
pass pass
# TODO: this function should probably return a list of environments since # TODO: this function should probably return a list of environments since
# multiple Python installations can be found on a system for the same version. # multiple Python installations can be found on a system for the same version.
def get_system_environment(version, **kwargs): def get_system_environment(version, *, env_vars={}):
""" """
Return the first Python environment found for a string of the form 'X.Y' Return the first Python environment found for a string of the form 'X.Y'
where X and Y are the major and minor versions of Python. where X and Y are the major and minor versions of Python.
@@ -360,26 +347,20 @@ def get_system_environment(version, **kwargs):
if os.name == 'nt': if os.name == 'nt':
for exe in _get_executables_from_windows_registry(version): for exe in _get_executables_from_windows_registry(version):
try: try:
return Environment(exe, **kwargs) return Environment(exe, env_vars=env_vars)
except InvalidPythonEnvironment: except InvalidPythonEnvironment:
pass pass
raise InvalidPythonEnvironment("Cannot find executable python%s." % version) raise InvalidPythonEnvironment("Cannot find executable python%s." % version)
def create_environment(path, safe=True, **kwargs): def create_environment(path, *, safe=True, env_vars=None):
""" """
Make it possible to manually create an Environment object by specifying a Make it possible to manually create an Environment object by specifying a
Virtualenv path or an executable path and optional environment variables. Virtualenv path or an executable path and optional environment variables.
:raises: :exc:`.InvalidPythonEnvironment` :raises: :exc:`.InvalidPythonEnvironment`
:returns: :class:`.Environment` :returns: :class:`.Environment`
TODO: make env_vars a kwarg when Python 2 is dropped. For now, preserve API
""" """
return _create_environment(path, safe, **kwargs)
def _create_environment(path, safe=True, env_vars=None):
if os.path.isfile(path): if os.path.isfile(path):
_assert_safe(path, safe) _assert_safe(path, safe)
return Environment(path, env_vars=env_vars) return Environment(path, env_vars=env_vars)
@@ -403,11 +384,7 @@ def _get_executable_path(path, safe=True):
def _get_executables_from_windows_registry(version): def _get_executables_from_windows_registry(version):
# The winreg module is named _winreg on Python 2. import winreg
try:
import winreg
except ImportError:
import _winreg as winreg
# TODO: support Python Anaconda. # TODO: support Python Anaconda.
sub_keys = [ sub_keys = [
+3 -4
View File
@@ -1,6 +1,5 @@
import os import os
from jedi._compatibility import FileNotFoundError, force_unicode, scandir
from jedi.api import classes from jedi.api import classes
from jedi.api.strings import StringName, get_quote_ending from jedi.api.strings import StringName, get_quote_ending
from jedi.api.helpers import match from jedi.api.helpers import match
@@ -8,7 +7,7 @@ from jedi.inference.helpers import get_str_or_none
class PathName(StringName): class PathName(StringName):
api_type = u'path' api_type = 'path'
def complete_file_name(inference_state, module_context, start_leaf, quote, string, def complete_file_name(inference_state, module_context, start_leaf, quote, string,
@@ -38,7 +37,7 @@ def complete_file_name(inference_state, module_context, start_leaf, quote, strin
string = to_be_added + string string = to_be_added + string
base_path = os.path.join(inference_state.project.path, string) base_path = os.path.join(inference_state.project.path, string)
try: try:
listed = sorted(scandir(base_path), key=lambda e: e.name) listed = sorted(os.scandir(base_path), key=lambda e: e.name)
# OSError: [Errno 36] File name too long: '...' # OSError: [Errno 36] File name too long: '...'
except (FileNotFoundError, OSError): except (FileNotFoundError, OSError):
return return
@@ -94,7 +93,7 @@ def _add_strings(context, nodes, add_slash=False):
return None return None
if not first and add_slash: if not first and add_slash:
string += os.path.sep string += os.path.sep
string += force_unicode(s) string += s
first = False first = False
return string return string
+11 -9
View File
@@ -6,11 +6,11 @@ from collections import namedtuple
from textwrap import dedent from textwrap import dedent
from itertools import chain from itertools import chain
from functools import wraps from functools import wraps
from inspect import Parameter
from parso.python.parser import Parser from parso.python.parser import Parser
from parso.python import tree from parso.python import tree
from jedi._compatibility import u, Parameter
from jedi.inference.base_value import NO_VALUES from jedi.inference.base_value import NO_VALUES
from jedi.inference.syntax_tree import infer_atom from jedi.inference.syntax_tree import infer_atom
from jedi.inference.helpers import infer_call_of_leaf from jedi.inference.helpers import infer_call_of_leaf
@@ -44,7 +44,10 @@ def match(string, like_name, fuzzy=False):
def sorted_definitions(defs): def sorted_definitions(defs):
# Note: `or ''` below is required because `module_path` could be # Note: `or ''` below is required because `module_path` could be
return sorted(defs, key=lambda x: (x.module_path or '', x.line or 0, x.column or 0, x.name)) return sorted(defs, key=lambda x: (str(x.module_path or ''),
x.line or 0,
x.column or 0,
x.name))
def get_on_completion_name(module_node, lines, position): def get_on_completion_name(module_node, lines, position):
@@ -84,18 +87,18 @@ def _get_code_for_stack(code_lines, leaf, position):
# If we're not on a comment simply get the previous leaf and proceed. # If we're not on a comment simply get the previous leaf and proceed.
leaf = leaf.get_previous_leaf() leaf = leaf.get_previous_leaf()
if leaf is None: if leaf is None:
return u('') # At the beginning of the file. return '' # At the beginning of the file.
is_after_newline = leaf.type == 'newline' is_after_newline = leaf.type == 'newline'
while leaf.type == 'newline': while leaf.type == 'newline':
leaf = leaf.get_previous_leaf() leaf = leaf.get_previous_leaf()
if leaf is None: if leaf is None:
return u('') return ''
if leaf.type == 'error_leaf' or leaf.type == 'string': if leaf.type == 'error_leaf' or leaf.type == 'string':
if leaf.start_pos[0] < position[0]: if leaf.start_pos[0] < position[0]:
# On a different line, we just begin anew. # On a different line, we just begin anew.
return u('') return ''
# Error leafs cannot be parsed, completion in strings is also # Error leafs cannot be parsed, completion in strings is also
# impossible. # impossible.
@@ -111,7 +114,7 @@ def _get_code_for_stack(code_lines, leaf, position):
if user_stmt.start_pos[1] > position[1]: if user_stmt.start_pos[1] > position[1]:
# This means that it's actually a dedent and that means that we # This means that it's actually a dedent and that means that we
# start without value (part of a suite). # start without value (part of a suite).
return u('') return ''
# This is basically getting the relevant lines. # This is basically getting the relevant lines.
return _get_code(code_lines, user_stmt.get_start_pos_of_prefix(), position) return _get_code(code_lines, user_stmt.get_start_pos_of_prefix(), position)
@@ -294,8 +297,7 @@ def _iter_arguments(nodes, position):
# Returns Generator[Tuple[star_count, Optional[key_start: str], had_equal]] # Returns Generator[Tuple[star_count, Optional[key_start: str], had_equal]]
nodes_before = [c for c in nodes if c.start_pos < position] nodes_before = [c for c in nodes if c.start_pos < position]
if nodes_before[-1].type == 'arglist': if nodes_before[-1].type == 'arglist':
for x in _iter_arguments(nodes_before[-1].children, position): yield from _iter_arguments(nodes_before[-1].children, position)
yield x # Python 2 :(
return return
previous_node_yielded = False previous_node_yielded = False
@@ -320,7 +322,7 @@ def _iter_arguments(nodes, position):
else: else:
yield 0, None, False yield 0, None, False
stars_seen = 0 stars_seen = 0
elif node.type in ('testlist', 'testlist_star_expr'): # testlist is Python 2 elif node.type == 'testlist_star_expr':
for n in node.children[::2]: for n in node.children[::2]:
if n.type == 'star_expr': if n.type == 'star_expr':
stars_seen = 1 stars_seen = 1
+1 -1
View File
@@ -47,7 +47,7 @@ class MixedParserTreeFilter(ParserTreeFilter):
class MixedModuleContext(ModuleContext): class MixedModuleContext(ModuleContext):
def __init__(self, tree_module_value, namespaces): def __init__(self, tree_module_value, namespaces):
super(MixedModuleContext, self).__init__(tree_module_value) super().__init__(tree_module_value)
self.mixed_values = [ self.mixed_values = [
self._get_mixed_object( self._get_mixed_object(
_create(self.inference_state, NamespaceObject(n)) _create(self.inference_state, NamespaceObject(n))
+4 -16
View File
@@ -1,22 +1,13 @@
import pydoc import pydoc
from contextlib import suppress
from jedi.inference.utils import ignored
from jedi.inference.names import AbstractArbitraryName from jedi.inference.names import AbstractArbitraryName
try: from pydoc_data import topics as pydoc_topics
from pydoc_data import topics as pydoc_topics
except ImportError:
# Python 2
try:
import pydoc_topics
except ImportError:
# This is for Python 3 embeddable version, which dont have
# pydoc_data module in its file python3x.zip.
pydoc_topics = None
class KeywordName(AbstractArbitraryName): class KeywordName(AbstractArbitraryName):
api_type = u'keyword' api_type = 'keyword'
def py__doc__(self): def py__doc__(self):
return imitate_pydoc(self.string_name) return imitate_pydoc(self.string_name)
@@ -30,11 +21,8 @@ def imitate_pydoc(string):
if pydoc_topics is None: if pydoc_topics is None:
return '' return ''
# str needed because of possible unicode stuff in py2k (pydoc doesn't work
# with unicode strings)
string = str(string)
h = pydoc.help h = pydoc.help
with ignored(KeyError): with suppress(KeyError):
# try to access symbols # try to access symbols
string = h.symbols[string] string = h.symbols[string]
string, _, related = string.partition(' ') string, _, related = string.partition(' ')
+49 -59
View File
@@ -7,26 +7,21 @@ flexibility to define sys paths and Python interpreters for a project,
Projects can be saved to disk and loaded again, to allow project definitions to Projects can be saved to disk and loaded again, to allow project definitions to
be used across repositories. be used across repositories.
""" """
import os
import errno
import json import json
import sys from pathlib import Path
from itertools import chain
from jedi._compatibility import FileNotFoundError, PermissionError, \
IsADirectoryError, NotADirectoryError
from jedi import debug from jedi import debug
from jedi.api.environment import get_cached_default_environment, create_environment from jedi.api.environment import get_cached_default_environment, create_environment
from jedi.api.exceptions import WrongVersion from jedi.api.exceptions import WrongVersion
from jedi.api.completion import search_in_module from jedi.api.completion import search_in_module
from jedi.api.helpers import split_search_string, get_module_names from jedi.api.helpers import split_search_string, get_module_names
from jedi._compatibility import force_unicode
from jedi.inference.imports import load_module_from_path, \ from jedi.inference.imports import load_module_from_path, \
load_namespace_from_path, iter_module_names load_namespace_from_path, iter_module_names
from jedi.inference.sys_path import discover_buildout_paths from jedi.inference.sys_path import discover_buildout_paths
from jedi.inference.cache import inference_state_as_method_param_cache from jedi.inference.cache import inference_state_as_method_param_cache
from jedi.inference.references import recurse_find_python_folders_and_files, search_in_file_ios from jedi.inference.references import recurse_find_python_folders_and_files, search_in_file_ios
from jedi.file_io import FolderIO from jedi.file_io import FolderIO
from jedi.common import traverse_parents
_CONFIG_FOLDER = '.jedi' _CONFIG_FOLDER = '.jedi'
_CONTAINS_POTENTIAL_PROJECT = \ _CONTAINS_POTENTIAL_PROJECT = \
@@ -61,10 +56,6 @@ def _remove_duplicates_from_path(path):
yield p yield p
def _force_unicode_list(lst):
return list(map(force_unicode, lst))
class Project(object): class Project(object):
""" """
Projects are a simple way to manage Python folders and define how Jedi does Projects are a simple way to manage Python folders and define how Jedi does
@@ -75,11 +66,11 @@ class Project(object):
@staticmethod @staticmethod
def _get_config_folder_path(base_path): def _get_config_folder_path(base_path):
return os.path.join(base_path, _CONFIG_FOLDER) return base_path.joinpath(_CONFIG_FOLDER)
@staticmethod @staticmethod
def _get_json_path(base_path): def _get_json_path(base_path):
return os.path.join(Project._get_config_folder_path(base_path), 'project.json') return Project._get_config_folder_path(base_path).joinpath('project.json')
@classmethod @classmethod
def load(cls, path): def load(cls, path):
@@ -89,6 +80,8 @@ class Project(object):
:param path: The path of the directory you want to use as a project. :param path: The path of the directory you want to use as a project.
""" """
if isinstance(path, str):
path = Path(path)
with open(cls._get_json_path(path)) as f: with open(cls._get_json_path(path)) as f:
version, data = json.load(f) version, data = json.load(f)
@@ -107,13 +100,9 @@ class Project(object):
data.pop('_environment', None) data.pop('_environment', None)
data.pop('_django', None) # TODO make django setting public? data.pop('_django', None) # TODO make django setting public?
data = {k.lstrip('_'): v for k, v in data.items()} data = {k.lstrip('_'): v for k, v in data.items()}
data['path'] = str(data['path'])
# TODO when dropping Python 2 use pathlib.Path.mkdir(parents=True, exist_ok=True) self._get_config_folder_path(self._path).mkdir(parents=True, exist_ok=True)
try:
os.makedirs(self._get_config_folder_path(self._path))
except OSError as e:
if e.errno != errno.EEXIST:
raise
with open(self._get_json_path(self._path), 'w') as f: with open(self._get_json_path(self._path), 'w') as f:
return json.dump((_SERIALIZER_VERSION, data), f) return json.dump((_SERIALIZER_VERSION, data), f)
@@ -138,14 +127,20 @@ class Project(object):
""" """
def py2_comp(path, environment_path=None, load_unsafe_extensions=False, def py2_comp(path, environment_path=None, load_unsafe_extensions=False,
sys_path=None, added_sys_path=(), smart_sys_path=True): sys_path=None, added_sys_path=(), smart_sys_path=True):
self._path = os.path.abspath(path) if isinstance(path, str):
path = Path(path).absolute()
self._path = path
self._environment_path = environment_path self._environment_path = environment_path
if sys_path is not None:
# Remap potential pathlib.Path entries
sys_path = list(map(str, sys_path))
self._sys_path = sys_path self._sys_path = sys_path
self._smart_sys_path = smart_sys_path self._smart_sys_path = smart_sys_path
self._load_unsafe_extensions = load_unsafe_extensions self._load_unsafe_extensions = load_unsafe_extensions
self._django = False self._django = False
self.added_sys_path = list(added_sys_path) # Remap potential pathlib.Path entries
self.added_sys_path = list(map(str, added_sys_path))
"""The sys path that is going to be added at the end of the """ """The sys path that is going to be added at the end of the """
py2_comp(path, **kwargs) py2_comp(path, **kwargs)
@@ -182,23 +177,27 @@ class Project(object):
sys_path = list(self._sys_path) sys_path = list(self._sys_path)
if self._smart_sys_path: if self._smart_sys_path:
prefixed.append(self._path) prefixed.append(str(self._path))
if inference_state.script_path is not None: if inference_state.script_path is not None:
suffixed += discover_buildout_paths(inference_state, inference_state.script_path) suffixed += map(str, discover_buildout_paths(
inference_state,
inference_state.script_path
))
if add_parent_paths: if add_parent_paths:
# Collect directories in upward search by: # Collect directories in upward search by:
# 1. Skipping directories with __init__.py # 1. Skipping directories with __init__.py
# 2. Stopping immediately when above self._path # 2. Stopping immediately when above self._path
traversed = [] traversed = []
for parent_path in traverse_parents(inference_state.script_path): for parent_path in inference_state.script_path.parents:
if parent_path == self._path or not parent_path.startswith(self._path): if parent_path == self._path \
or self._path not in parent_path.parents:
break break
if not add_init_paths \ if not add_init_paths \
and os.path.isfile(os.path.join(parent_path, "__init__.py")): and parent_path.joinpath("__init__.py").is_file():
continue continue
traversed.append(parent_path) traversed.append(str(parent_path))
# AFAIK some libraries have imports like `foo.foo.bar`, which # AFAIK some libraries have imports like `foo.foo.bar`, which
# leads to the conclusion to by default prefer longer paths # leads to the conclusion to by default prefer longer paths
@@ -206,10 +205,10 @@ class Project(object):
suffixed += reversed(traversed) suffixed += reversed(traversed)
if self._django: if self._django:
prefixed.append(self._path) prefixed.append(str(self._path))
path = prefixed + sys_path + suffixed path = prefixed + sys_path + suffixed
return list(_force_unicode_list(_remove_duplicates_from_path(path))) return list(_remove_duplicates_from_path(path))
def get_environment(self): def get_environment(self):
if self._environment is None: if self._environment is None:
@@ -219,7 +218,7 @@ class Project(object):
self._environment = get_cached_default_environment() self._environment = get_cached_default_environment()
return self._environment return self._environment
def search(self, string, **kwargs): def search(self, string, *, all_scopes=False):
""" """
Searches a name in the whole project. If the project is very big, Searches a name in the whole project. If the project is very big,
at some point Jedi will stop searching. However it's also very much at some point Jedi will stop searching. However it's also very much
@@ -240,7 +239,7 @@ class Project(object):
functions and classes. functions and classes.
:yields: :class:`.Name` :yields: :class:`.Name`
""" """
return self._search(string, **kwargs) return self._search_func(string, all_scopes=all_scopes)
def complete_search(self, string, **kwargs): def complete_search(self, string, **kwargs):
""" """
@@ -254,9 +253,6 @@ class Project(object):
""" """
return self._search_func(string, complete=True, **kwargs) return self._search_func(string, complete=True, **kwargs)
def _search(self, string, all_scopes=False): # Python 2..
return self._search_func(string, all_scopes=all_scopes)
@_try_to_skip_duplicates @_try_to_skip_duplicates
def _search_func(self, string, complete=False, all_scopes=False): def _search_func(self, string, complete=False, all_scopes=False):
# Using a Script is they easiest way to get an empty module context. # Using a Script is they easiest way to get an empty module context.
@@ -265,16 +261,12 @@ class Project(object):
inference_state = s._inference_state inference_state = s._inference_state
empty_module_context = s._get_module_context() empty_module_context = s._get_module_context()
if inference_state.grammar.version_info < (3, 6) or sys.version_info < (3, 6):
raise NotImplementedError(
"No support for refactorings/search on Python 2/3.5"
)
debug.dbg('Search for string %s, complete=%s', string, complete) debug.dbg('Search for string %s, complete=%s', string, complete)
wanted_type, wanted_names = split_search_string(string) wanted_type, wanted_names = split_search_string(string)
name = wanted_names[0] name = wanted_names[0]
stub_folder_name = name + '-stubs' stub_folder_name = name + '-stubs'
ios = recurse_find_python_folders_and_files(FolderIO(self._path)) ios = recurse_find_python_folders_and_files(FolderIO(str(self._path)))
file_ios = [] file_ios = []
# 1. Search for modules in the current project # 1. Search for modules in the current project
@@ -295,14 +287,13 @@ class Project(object):
continue continue
else: else:
file_ios.append(file_io) file_ios.append(file_io)
file_name = os.path.basename(file_io.path) if Path(file_io.path).name in (name + '.py', name + '.pyi'):
if file_name in (name + '.py', name + '.pyi'):
m = load_module_from_path(inference_state, file_io).as_context() m = load_module_from_path(inference_state, file_io).as_context()
else: else:
continue continue
debug.dbg('Search of a specific module %s', m) debug.dbg('Search of a specific module %s', m)
for x in search_in_module( yield from search_in_module(
inference_state, inference_state,
m, m,
names=[m.name], names=[m.name],
@@ -311,15 +302,14 @@ class Project(object):
complete=complete, complete=complete,
convert=True, convert=True,
ignore_imports=True, ignore_imports=True,
): )
yield x # Python 2...
# 2. Search for identifiers in the project. # 2. Search for identifiers in the project.
for module_context in search_in_file_ios(inference_state, file_ios, name): for module_context in search_in_file_ios(inference_state, file_ios, name):
names = get_module_names(module_context.tree_node, all_scopes=all_scopes) names = get_module_names(module_context.tree_node, all_scopes=all_scopes)
names = [module_context.create_name(n) for n in names] names = [module_context.create_name(n) for n in names]
names = _remove_imports(names) names = _remove_imports(names)
for x in search_in_module( yield from search_in_module(
inference_state, inference_state,
module_context, module_context,
names=names, names=names,
@@ -327,18 +317,17 @@ class Project(object):
wanted_names=wanted_names, wanted_names=wanted_names,
complete=complete, complete=complete,
ignore_imports=True, ignore_imports=True,
): )
yield x # Python 2...
# 3. Search for modules on sys.path # 3. Search for modules on sys.path
sys_path = [ sys_path = [
p for p in self._get_sys_path(inference_state) p for p in self._get_sys_path(inference_state)
# Exclude folders that are handled by recursing of the Python # Exclude folders that are handled by recursing of the Python
# folders. # folders.
if not p.startswith(self._path) if not p.startswith(str(self._path))
] ]
names = list(iter_module_names(inference_state, empty_module_context, sys_path)) names = list(iter_module_names(inference_state, empty_module_context, sys_path))
for x in search_in_module( yield from search_in_module(
inference_state, inference_state,
empty_module_context, empty_module_context,
names=names, names=names,
@@ -346,8 +335,7 @@ class Project(object):
wanted_names=wanted_names, wanted_names=wanted_names,
complete=complete, complete=complete,
convert=True, convert=True,
): )
yield x # Python 2...
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self._path) return '<%s: %s>' % (self.__class__.__name__, self._path)
@@ -355,7 +343,7 @@ class Project(object):
def _is_potential_project(path): def _is_potential_project(path):
for name in _CONTAINS_POTENTIAL_PROJECT: for name in _CONTAINS_POTENTIAL_PROJECT:
if os.path.exists(os.path.join(path, name)): if path.joinpath(name).exists():
return True return True
return False return False
@@ -363,7 +351,7 @@ def _is_potential_project(path):
def _is_django_path(directory): def _is_django_path(directory):
""" Detects the path of the very well known Django library (if used) """ """ Detects the path of the very well known Django library (if used) """
try: try:
with open(os.path.join(directory, 'manage.py'), 'rb') as f: with open(directory.joinpath('manage.py'), 'rb') as f:
return b"DJANGO_SETTINGS_MODULE" in f.read() return b"DJANGO_SETTINGS_MODULE" in f.read()
except (FileNotFoundError, IsADirectoryError, PermissionError): except (FileNotFoundError, IsADirectoryError, PermissionError):
return False return False
@@ -380,12 +368,14 @@ def get_default_project(path=None):
``requirements.txt`` and ``MANIFEST.in``. ``requirements.txt`` and ``MANIFEST.in``.
""" """
if path is None: if path is None:
path = os.getcwd() path = Path.cwd()
elif isinstance(path, str):
path = Path(path)
check = os.path.realpath(path) check = path.absolute()
probable_path = None probable_path = None
first_no_init_file = None first_no_init_file = None
for dir in traverse_parents(check, include_current=True): for dir in chain([check], check.parents):
try: try:
return Project.load(dir) return Project.load(dir)
except (FileNotFoundError, IsADirectoryError, PermissionError): except (FileNotFoundError, IsADirectoryError, PermissionError):
@@ -394,11 +384,11 @@ def get_default_project(path=None):
continue continue
if first_no_init_file is None: if first_no_init_file is None:
if os.path.exists(os.path.join(dir, '__init__.py')): if dir.joinpath('__init__.py').exists():
# In the case that a __init__.py exists, it's in 99% just a # In the case that a __init__.py exists, it's in 99% just a
# Python package and the project sits at least one level above. # Python package and the project sits at least one level above.
continue continue
else: elif not dir.is_file():
first_no_init_file = dir first_no_init_file = dir
if _is_django_path(dir): if _is_django_path(dir):
@@ -416,7 +406,7 @@ def get_default_project(path=None):
if first_no_init_file is not None: if first_no_init_file is not None:
return Project(first_no_init_file) return Project(first_no_init_file)
curdir = path if os.path.isdir(path) else os.path.dirname(path) curdir = path if path.is_dir() else path.parent
return Project(curdir) return Project(curdir)
+19 -24
View File
@@ -1,7 +1,6 @@
from os.path import dirname, basename, join, relpath
import os
import re
import difflib import difflib
from pathlib import Path
from typing import Dict
from parso import split_lines from parso import split_lines
@@ -43,15 +42,15 @@ class ChangedFile(object):
if self._from_path is None: if self._from_path is None:
from_p = '' from_p = ''
else: else:
from_p = relpath(self._from_path, project_path) from_p = self._from_path.relative_to(project_path)
if self._to_path is None: if self._to_path is None:
to_p = '' to_p = ''
else: else:
to_p = relpath(self._to_path, project_path) to_p = self._to_path.relative_to(project_path)
diff = difflib.unified_diff( diff = difflib.unified_diff(
old_lines, new_lines, old_lines, new_lines,
fromfile=from_p, fromfile=str(from_p),
tofile=to_p, tofile=str(to_p),
) )
# Apparently there's a space at the end of the diff - for whatever # Apparently there's a space at the end of the diff - for whatever
# reason. # reason.
@@ -79,17 +78,15 @@ class Refactoring(object):
self._renames = renames self._renames = renames
self._file_to_node_changes = file_to_node_changes self._file_to_node_changes = file_to_node_changes
def get_changed_files(self): def get_changed_files(self) -> Dict[Path, ChangedFile]:
"""
Returns a path to ``ChangedFile`` map.
"""
def calculate_to_path(p): def calculate_to_path(p):
if p is None: if p is None:
return p return p
p = str(p)
for from_, to in renames: for from_, to in renames:
if p.startswith(from_): if p.startswith(str(from_)):
p = to + p[len(from_):] p = str(to) + p[len(str(from_)):]
return p return Path(p)
renames = self.get_renames() renames = self.get_renames()
return { return {
@@ -115,7 +112,7 @@ class Refactoring(object):
project_path = self._inference_state.project.path project_path = self._inference_state.project.path
for from_, to in self.get_renames(): for from_, to in self.get_renames():
text += 'rename from %s\nrename to %s\n' \ text += 'rename from %s\nrename to %s\n' \
% (relpath(from_, project_path), relpath(to, project_path)) % (from_.relative_to(project_path), to.relative_to(project_path))
return text + ''.join(f.get_diff() for f in self.get_changed_files().values()) return text + ''.join(f.get_diff() for f in self.get_changed_files().values())
@@ -127,17 +124,14 @@ class Refactoring(object):
f.apply() f.apply()
for old, new in self.get_renames(): for old, new in self.get_renames():
os.rename(old, new) old.rename(new)
def _calculate_rename(path, new_name): def _calculate_rename(path, new_name):
name = basename(path) dir_ = path.parent
dir_ = dirname(path) if path.name in ('__init__.py', '__init__.pyi'):
if name in ('__init__.py', '__init__.pyi'): return dir_, dir_.parent.joinpath(new_name)
parent_dir = dirname(dir_) return path, dir_.joinpath(new_name + path.suffix)
return dir_, join(parent_dir, new_name)
ending = re.search(r'\.pyi?$', name).group(0)
return path, join(dir_, new_name + ending)
def rename(inference_state, definitions, new_name): def rename(inference_state, definitions, new_name):
@@ -150,7 +144,8 @@ def rename(inference_state, definitions, new_name):
for d in definitions: for d in definitions:
tree_name = d._name.tree_name tree_name = d._name.tree_name
if d.type == 'module' and tree_name is None: if d.type == 'module' and tree_name is None:
file_renames.add(_calculate_rename(d.module_path, new_name)) p = None if d.module_path is None else Path(d.module_path)
file_renames.add(_calculate_rename(p, new_name))
else: else:
# This private access is ok in a way. It's not public to # This private access is ok in a way. It's not public to
# protect Jedi users from seeing it. # protect Jedi users from seeing it.
+1 -2
View File
@@ -350,8 +350,7 @@ def _find_non_global_names(nodes):
if node.type == 'trailer' and node.children[0] == '.': if node.type == 'trailer' and node.children[0] == '.':
continue continue
for x in _find_non_global_names(children): # Python 2... yield from _find_non_global_names(children)
yield x
def _get_code_insertion_node(node, is_bound_method): def _get_code_insertion_node(node, is_bound_method):
+1 -1
View File
@@ -9,7 +9,7 @@ just use IPython instead::
Then you will be able to use Jedi completer in your Python interpreter:: Then you will be able to use Jedi completer in your Python interpreter::
$ python $ python
Python 2.7.2+ (default, Jul 20 2012, 22:15:08) Python 3.9.2+ (default, Jul 20 2020, 22:15:08)
[GCC 4.6.1] on linux2 [GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information. Type "help", "copyright", "credits" or "license" for more information.
>>> import os >>> import os
+2 -3
View File
@@ -9,7 +9,6 @@ names in a module, but pretty much an arbitrary string.
""" """
import re import re
from jedi._compatibility import unicode
from jedi.inference.names import AbstractArbitraryName from jedi.inference.names import AbstractArbitraryName
from jedi.inference.helpers import infer_call_of_leaf from jedi.inference.helpers import infer_call_of_leaf
from jedi.api.classes import Completion from jedi.api.classes import Completion
@@ -19,7 +18,7 @@ _sentinel = object()
class StringName(AbstractArbitraryName): class StringName(AbstractArbitraryName):
api_type = u'string' api_type = 'string'
is_value_name = False is_value_name = False
@@ -65,7 +64,7 @@ def _completions_for_dicts(inference_state, dicts, literal_string, cut_end_quote
def _create_repr_string(literal_string, dict_key): def _create_repr_string(literal_string, dict_key):
if not isinstance(dict_key, (unicode, bytes)) or not literal_string: if not isinstance(dict_key, (str, bytes)) or not literal_string:
return repr(dict_key) return repr(dict_key)
r = repr(dict_key) r = repr(dict_key)
-12
View File
@@ -1,18 +1,6 @@
import os
from contextlib import contextmanager from contextlib import contextmanager
def traverse_parents(path, include_current=False):
if not include_current:
path = os.path.dirname(path)
previous = None
while previous != path:
yield path
previous = path
path = os.path.dirname(path)
@contextmanager @contextmanager
def monkeypatch(obj, attribute_name, new_value): def monkeypatch(obj, attribute_name, new_value):
""" """
+3 -12
View File
@@ -2,8 +2,6 @@ import os
import time import time
from contextlib import contextmanager from contextlib import contextmanager
from jedi._compatibility import encoding, is_py3, u
_inited = False _inited = False
@@ -97,16 +95,14 @@ def increase_indent_cm(title=None, color='MAGENTA'):
dbg('End: ' + title, color=color) dbg('End: ' + title, color=color)
def dbg(message, *args, **kwargs): def dbg(message, *args, color='GREEN'):
""" Looks at the stack, to see if a debug message should be printed. """ """ Looks at the stack, to see if a debug message should be printed. """
# Python 2 compatibility, because it doesn't understand default args
color = kwargs.pop('color', 'GREEN')
assert color assert color
if debug_function and enable_notice: if debug_function and enable_notice:
i = ' ' * _debug_indent i = ' ' * _debug_indent
_lazy_colorama_init() _lazy_colorama_init()
debug_function(color, i + 'dbg: ' + message % tuple(u(repr(a)) for a in args)) debug_function(color, i + 'dbg: ' + message % tuple(repr(a) for a in args))
def warning(message, *args, **kwargs): def warning(message, *args, **kwargs):
@@ -116,7 +112,7 @@ def warning(message, *args, **kwargs):
if debug_function and enable_warning: if debug_function and enable_warning:
i = ' ' * _debug_indent i = ' ' * _debug_indent
if format: if format:
message = message % tuple(u(repr(a)) for a in args) message = message % tuple(repr(a) for a in args)
debug_function('RED', i + 'warning: ' + message) debug_function('RED', i + 'warning: ' + message)
@@ -135,9 +131,4 @@ def print_to_stdout(color, str_out):
""" """
col = getattr(Fore, color) col = getattr(Fore, color)
_lazy_colorama_init() _lazy_colorama_init()
if not is_py3:
str_out = str_out.encode(encoding, 'replace')
print(col + str_out + Fore.RESET) print(col + str_out + Fore.RESET)
# debug_function = print_to_stdout
+2 -2
View File
@@ -65,13 +65,13 @@ class FileIOFolderMixin(object):
class ZipFileIO(file_io.KnownContentFileIO, FileIOFolderMixin): class ZipFileIO(file_io.KnownContentFileIO, FileIOFolderMixin):
"""For .zip and .egg archives""" """For .zip and .egg archives"""
def __init__(self, path, code, zip_path): def __init__(self, path, code, zip_path):
super(ZipFileIO, self).__init__(path, code) super().__init__(path, code)
self._zip_path = zip_path self._zip_path = zip_path
def get_last_modified(self): def get_last_modified(self):
try: try:
return os.path.getmtime(self._zip_path) return os.path.getmtime(self._zip_path)
except OSError: # Python 3 would probably only need FileNotFoundError except (FileNotFoundError, PermissionError, NotADirectoryError):
return None return None
+6 -6
View File
@@ -123,16 +123,14 @@ class InferenceState(object):
@property @property
@inference_state_function_cache() @inference_state_function_cache()
def builtins_module(self): def builtins_module(self):
module_name = u'builtins' module_name = 'builtins'
if self.environment.version_info.major == 2:
module_name = u'__builtin__'
builtins_module, = self.import_module((module_name,), sys_path=()) builtins_module, = self.import_module((module_name,), sys_path=())
return builtins_module return builtins_module
@property @property
@inference_state_function_cache() @inference_state_function_cache()
def typing_module(self): def typing_module(self):
typing_module, = self.import_module((u'typing',)) typing_module, = self.import_module(('typing',))
return typing_module return typing_module
def reset_recursion_limitations(self): def reset_recursion_limitations(self):
@@ -178,14 +176,16 @@ class InferenceState(object):
return helpers.infer_call_of_leaf(context, name) return helpers.infer_call_of_leaf(context, name)
def parse_and_get_code(self, code=None, path=None, encoding='utf-8', def parse_and_get_code(self, code=None, path=None,
use_latest_grammar=False, file_io=None, **kwargs): use_latest_grammar=False, file_io=None, **kwargs):
if path is not None:
path = str(path)
if code is None: if code is None:
if file_io is None: if file_io is None:
file_io = FileIO(path) file_io = FileIO(path)
code = file_io.read() code = file_io.read()
# We cannot just use parso, because it doesn't use errors='replace'. # We cannot just use parso, because it doesn't use errors='replace'.
code = parso.python_bytes_to_unicode(code, encoding=encoding, errors='replace') code = parso.python_bytes_to_unicode(code, encoding='utf-8', errors='replace')
if len(code) > settings._cropped_file_size: if len(code) > settings._cropped_file_size:
code = code[:settings._cropped_file_size] code = code[:settings._cropped_file_size]
+2 -6
View File
@@ -3,7 +3,6 @@ Module for statical analysis.
""" """
from parso.python import tree from parso.python import tree
from jedi._compatibility import force_unicode
from jedi import debug from jedi import debug
from jedi.inference.helpers import is_string from jedi.inference.helpers import is_string
@@ -50,13 +49,10 @@ class Error(object):
first = self.__class__.__name__[0] first = self.__class__.__name__[0]
return first + str(CODES[self.name][0]) return first + str(CODES[self.name][0])
def __unicode__(self): def __str__(self):
return '%s:%s:%s: %s %s' % (self.path, self.line, self.column, return '%s:%s:%s: %s %s' % (self.path, self.line, self.column,
self.code, self.message) self.code, self.message)
def __str__(self):
return self.__unicode__()
def __eq__(self, other): def __eq__(self, other):
return (self.path == other.path and self.name == other.name return (self.path == other.path and self.name == other.name
and self._start_pos == other._start_pos) and self._start_pos == other._start_pos)
@@ -193,7 +189,7 @@ def _check_for_exception_catch(node_context, jedi_name, exception, payload=None)
key, lazy_value = unpacked_args[1] key, lazy_value = unpacked_args[1]
names = list(lazy_value.infer()) names = list(lazy_value.infer())
assert len(names) == 1 and is_string(names[0]) assert len(names) == 1 and is_string(names[0])
assert force_unicode(names[0].get_safe_value()) == payload[1].value assert names[0].get_safe_value() == payload[1].value
# Check objects # Check objects
key, lazy_value = unpacked_args[0] key, lazy_value = unpacked_args[0]
+3 -8
View File
@@ -1,8 +1,8 @@
import re import re
from itertools import zip_longest
from parso.python import tree from parso.python import tree
from jedi._compatibility import zip_longest
from jedi import debug from jedi import debug
from jedi.inference.utils import PushBackIterator from jedi.inference.utils import PushBackIterator
from jedi.inference import analysis from jedi.inference import analysis
@@ -142,11 +142,8 @@ def unpack_arglist(arglist):
if arglist is None: if arglist is None:
return return
# Allow testlist here as well for Python2's class inheritance if arglist.type != 'arglist' and not (
# definitions. arglist.type == 'argument' and arglist.children[0] in ('*', '**')):
if not (arglist.type in ('arglist', 'testlist') or (
# in python 3.5 **arg is an argument, not arglist
arglist.type == 'argument' and arglist.children[0] in ('*', '**'))):
yield 0, arglist yield 0, arglist
return return
@@ -189,8 +186,6 @@ class TreeArguments(AbstractArguments):
iterators = [_iterate_star_args(self.context, a, el, funcdef) iterators = [_iterate_star_args(self.context, a, el, funcdef)
for a in arrays] for a in arrays]
for values in list(zip_longest(*iterators)): for values in list(zip_longest(*iterators)):
# TODO zip_longest yields None, that means this would raise
# an exception?
yield None, get_merged_lazy_value( yield None, get_merged_lazy_value(
[v for v in values if v is not None] [v for v in values if v is not None]
) )
+5 -4
View File
@@ -8,10 +8,11 @@ just one.
""" """
from functools import reduce from functools import reduce
from operator import add from operator import add
from itertools import zip_longest
from parso.python.tree import Name from parso.python.tree import Name
from jedi import debug from jedi import debug
from jedi._compatibility import zip_longest, unicode
from jedi.parser_utils import clean_scope_docstring from jedi.parser_utils import clean_scope_docstring
from jedi.inference.helpers import SimpleGetItemNotFound from jedi.inference.helpers import SimpleGetItemNotFound
from jedi.inference.utils import safe_property from jedi.inference.utils import safe_property
@@ -92,7 +93,7 @@ class HelperValueMixin(object):
return values return values
def py__await__(self): def py__await__(self):
await_value_set = self.py__getattribute__(u"__await__") await_value_set = self.py__getattribute__("__await__")
if not await_value_set: if not await_value_set:
debug.warning('Tried to run __await__ on value %s', self) debug.warning('Tried to run __await__ on value %s', self)
return await_value_set.execute_with_values() return await_value_set.execute_with_values()
@@ -357,7 +358,7 @@ class ValueWrapper(_ValueWrapperBase):
class TreeValue(Value): class TreeValue(Value):
def __init__(self, inference_state, parent_context, tree_node): def __init__(self, inference_state, parent_context, tree_node):
super(TreeValue, self).__init__(inference_state, parent_context) super().__init__(inference_state, parent_context)
self.tree_node = tree_node self.tree_node = tree_node
def __repr__(self): def __repr__(self):
@@ -385,7 +386,7 @@ def _getitem(value, index_values, contextualized_node):
unused_values = set() unused_values = set()
for index_value in index_values: for index_value in index_values:
index = index_value.get_safe_value(default=None) index = index_value.get_safe_value(default=None)
if type(index) in (float, int, str, unicode, slice, bytes): if type(index) in (float, int, str, slice, bytes):
try: try:
result |= value.py__simple_getitem__(index) result |= value.py__simple_getitem__(index)
continue continue
+1 -1
View File
@@ -78,7 +78,7 @@ class CachedMetaClass(type):
""" """
@inference_state_as_method_param_cache() @inference_state_as_method_param_cache()
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
return super(CachedMetaClass, self).__call__(*args, **kwargs) return super().__call__(*args, **kwargs)
def inference_state_method_generator_cache(): def inference_state_method_generator_cache():
+3 -4
View File
@@ -1,4 +1,3 @@
from jedi._compatibility import unicode
from jedi.inference.compiled.value import CompiledValue, CompiledName, \ from jedi.inference.compiled.value import CompiledValue, CompiledName, \
CompiledValueFilter, CompiledValueName, create_from_access_path CompiledValueFilter, CompiledValueName, create_from_access_path
from jedi.inference.base_value import LazyValueWrapper from jedi.inference.base_value import LazyValueWrapper
@@ -29,7 +28,7 @@ class ExactValue(LazyValueWrapper):
if name in ('get_safe_value', 'execute_operation', 'access_handle', if name in ('get_safe_value', 'execute_operation', 'access_handle',
'negate', 'py__bool__', 'is_compiled'): 'negate', 'py__bool__', 'is_compiled'):
return getattr(self._compiled_value, name) return getattr(self._compiled_value, name)
return super(ExactValue, self).__getattribute__(name) return super().__getattribute__(name)
def _get_wrapped_value(self): def _get_wrapped_value(self):
instance, = builtin_from_name( instance, = builtin_from_name(
@@ -45,7 +44,7 @@ def create_simple_object(inference_state, obj):
Only allows creations of objects that are easily picklable across Python Only allows creations of objects that are easily picklable across Python
versions. versions.
""" """
assert type(obj) in (int, float, str, bytes, unicode, slice, complex, bool), obj assert type(obj) in (int, float, str, bytes, slice, complex, bool), repr(obj)
compiled_value = create_from_access_path( compiled_value = create_from_access_path(
inference_state, inference_state,
inference_state.compiled_subprocess.create_simple_object(obj) inference_state.compiled_subprocess.create_simple_object(obj)
@@ -54,7 +53,7 @@ def create_simple_object(inference_state, obj):
def get_string_value_set(inference_state): def get_string_value_set(inference_state):
return builtin_from_name(inference_state, u'str').execute_with_values() return builtin_from_name(inference_state, 'str').execute_with_values()
def load_module(inference_state, dotted_name, **kwargs): def load_module(inference_state, dotted_name, **kwargs):
+22 -57
View File
@@ -1,4 +1,3 @@
from __future__ import print_function
import inspect import inspect
import types import types
import sys import sys
@@ -6,12 +5,12 @@ import operator as op
from collections import namedtuple from collections import namedtuple
import warnings import warnings
import re import re
import builtins
import typing
from jedi._compatibility import unicode, is_py3, builtins, \
py_version, force_unicode
from jedi.inference.compiled.getattr_static import getattr_static from jedi.inference.compiled.getattr_static import getattr_static
ALLOWED_GETITEM_TYPES = (str, list, tuple, unicode, bytes, bytearray, dict) ALLOWED_GETITEM_TYPES = (str, list, tuple, bytes, bytearray, dict)
MethodDescriptorType = type(str.replace) MethodDescriptorType = type(str.replace)
# These are not considered classes and access is granted even though they have # These are not considered classes and access is granted even though they have
@@ -28,17 +27,12 @@ NOT_CLASS_TYPES = (
types.MethodType, types.MethodType,
types.ModuleType, types.ModuleType,
types.TracebackType, types.TracebackType,
MethodDescriptorType MethodDescriptorType,
types.MappingProxyType,
types.SimpleNamespace,
types.DynamicClassAttribute,
) )
if is_py3:
NOT_CLASS_TYPES += (
types.MappingProxyType,
types.SimpleNamespace,
types.DynamicClassAttribute,
)
# Those types don't exist in typing. # Those types don't exist in typing.
MethodDescriptorType = type(str.replace) MethodDescriptorType = type(str.replace)
WrapperDescriptorType = type(set.__iter__) WrapperDescriptorType = type(set.__iter__)
@@ -144,35 +138,22 @@ class AccessPath(object):
def __init__(self, accesses): def __init__(self, accesses):
self.accesses = accesses self.accesses = accesses
# Writing both of these methods here looks a bit ridiculous. However with
# the differences of Python 2/3 it's actually necessary, because we will
# otherwise have a accesses attribute that is bytes instead of unicode.
def __getstate__(self):
return self.accesses
def __setstate__(self, value):
self.accesses = value
def create_access_path(inference_state, obj): def create_access_path(inference_state, obj):
access = create_access(inference_state, obj) access = create_access(inference_state, obj)
return AccessPath(access.get_access_path_tuples()) return AccessPath(access.get_access_path_tuples())
def _force_unicode_decorator(func):
return lambda *args, **kwargs: force_unicode(func(*args, **kwargs))
def get_api_type(obj): def get_api_type(obj):
if inspect.isclass(obj): if inspect.isclass(obj):
return u'class' return 'class'
elif inspect.ismodule(obj): elif inspect.ismodule(obj):
return u'module' return 'module'
elif inspect.isbuiltin(obj) or inspect.ismethod(obj) \ elif inspect.isbuiltin(obj) or inspect.ismethod(obj) \
or inspect.ismethoddescriptor(obj) or inspect.isfunction(obj): or inspect.ismethoddescriptor(obj) or inspect.isfunction(obj):
return u'function' return 'function'
# Everything else... # Everything else...
return u'instance' return 'instance'
class DirectObjectAccess(object): class DirectObjectAccess(object):
@@ -199,7 +180,7 @@ class DirectObjectAccess(object):
return None return None
def py__doc__(self): def py__doc__(self):
return force_unicode(inspect.getdoc(self._obj)) or u'' return inspect.getdoc(self._obj) or ''
def py__name__(self): def py__name__(self):
if not _is_class_instance(self._obj) or \ if not _is_class_instance(self._obj) or \
@@ -214,7 +195,7 @@ class DirectObjectAccess(object):
return None return None
try: try:
return force_unicode(cls.__name__) return cls.__name__
except AttributeError: except AttributeError:
return None return None
@@ -260,26 +241,23 @@ class DirectObjectAccess(object):
# Avoid some weird hacks that would just fail, because they cannot be # Avoid some weird hacks that would just fail, because they cannot be
# used by pickle. # used by pickle.
if not isinstance(paths, list) \ if not isinstance(paths, list) \
or not all(isinstance(p, (bytes, unicode)) for p in paths): or not all(isinstance(p, str) for p in paths):
return None return None
return paths return paths
@_force_unicode_decorator
@shorten_repr @shorten_repr
def get_repr(self): def get_repr(self):
builtins = 'builtins', '__builtin__'
if inspect.ismodule(self._obj): if inspect.ismodule(self._obj):
return repr(self._obj) return repr(self._obj)
# Try to avoid execution of the property. # Try to avoid execution of the property.
if safe_getattr(self._obj, '__module__', default='') in builtins: if safe_getattr(self._obj, '__module__', default='') == 'builtins':
return repr(self._obj) return repr(self._obj)
type_ = type(self._obj) type_ = type(self._obj)
if type_ == type: if type_ == type:
return type.__repr__(self._obj) return type.__repr__(self._obj)
if safe_getattr(type_, '__module__', default='') in builtins: if safe_getattr(type_, '__module__', default='') == 'builtins':
# Allow direct execution of repr for builtins. # Allow direct execution of repr for builtins.
return repr(self._obj) return repr(self._obj)
return object.__repr__(self._obj) return object.__repr__(self._obj)
@@ -310,10 +288,10 @@ class DirectObjectAccess(object):
name = try_to_get_name(type(self._obj)) name = try_to_get_name(type(self._obj))
if name is None: if name is None:
return () return ()
return tuple(force_unicode(n) for n in name.split('.')) return tuple(name.split('.'))
def dir(self): def dir(self):
return list(map(force_unicode, dir(self._obj))) return dir(self._obj)
def has_iter(self): def has_iter(self):
try: try:
@@ -396,7 +374,7 @@ class DirectObjectAccess(object):
return [self._create_access(module), access] return [self._create_access(module), access]
def get_safe_value(self): def get_safe_value(self):
if type(self._obj) in (bool, bytes, float, int, str, unicode, slice) or self._obj is None: if type(self._obj) in (bool, bytes, float, int, str, slice) or self._obj is None:
return self._obj return self._obj
raise ValueError("Object is type %s and not simple" % type(self._obj)) raise ValueError("Object is type %s and not simple" % type(self._obj))
@@ -464,9 +442,6 @@ class DirectObjectAccess(object):
""" """
Returns Tuple[Optional[str], Tuple[AccessPath, ...]] Returns Tuple[Optional[str], Tuple[AccessPath, ...]]
""" """
if sys.version_info < (3, 5):
return None, ()
name = None name = None
args = () args = ()
if safe_getattr(self._obj, '__module__', default='') == 'typing': if safe_getattr(self._obj, '__module__', default='') == 'typing':
@@ -485,8 +460,6 @@ class DirectObjectAccess(object):
return inspect.isclass(self._obj) and self._obj != type return inspect.isclass(self._obj) and self._obj != type
def _annotation_to_str(self, annotation): def _annotation_to_str(self, annotation):
if py_version < 30:
return ''
return inspect.formatannotation(annotation) return inspect.formatannotation(annotation)
def get_signature_params(self): def get_signature_params(self):
@@ -505,8 +478,6 @@ class DirectObjectAccess(object):
def _get_signature(self): def _get_signature(self):
obj = self._obj obj = self._obj
if py_version < 33:
raise ValueError("inspect.signature was introduced in 3.3")
try: try:
return inspect.signature(obj) return inspect.signature(obj)
except (RuntimeError, TypeError): except (RuntimeError, TypeError):
@@ -525,15 +496,9 @@ class DirectObjectAccess(object):
return None return None
try: try:
# Python 2 doesn't have typing. o = typing.get_type_hints(self._obj).get('return')
import typing except Exception:
except ImportError:
pass pass
else:
try:
o = typing.get_type_hints(self._obj).get('return')
except Exception:
pass
return self._create_access_path(o) return self._create_access_path(o)
@@ -546,7 +511,7 @@ class DirectObjectAccess(object):
objects of an objects objects of an objects
""" """
tuples = dict( tuples = dict(
(force_unicode(name), self.is_allowed_getattr(name)) (name, self.is_allowed_getattr(name))
for name in self.dir() for name in self.dir()
) )
return self.needs_type_completions(), tuples return self.needs_type_completions(), tuples
+4 -67
View File
@@ -7,7 +7,6 @@ information returned to enable Jedi to make decisions.
import types import types
from jedi import debug from jedi import debug
from jedi._compatibility import py_version
_sentinel = object() _sentinel = object()
@@ -39,7 +38,7 @@ def _is_type(obj):
return True return True
def _shadowed_dict_newstyle(klass): def _shadowed_dict(klass):
dict_attr = type.__dict__["__dict__"] dict_attr = type.__dict__["__dict__"]
for entry in _static_getmro(klass): for entry in _static_getmro(klass):
try: try:
@@ -54,7 +53,7 @@ def _shadowed_dict_newstyle(klass):
return _sentinel return _sentinel
def _static_getmro_newstyle(klass): def _static_getmro(klass):
mro = type.__dict__['__mro__'].__get__(klass) mro = type.__dict__['__mro__'].__get__(klass)
if not isinstance(mro, (tuple, list)): if not isinstance(mro, (tuple, list)):
# There are unfortunately no tests for this, I was not able to # There are unfortunately no tests for this, I was not able to
@@ -65,70 +64,8 @@ def _static_getmro_newstyle(klass):
return mro return mro
if py_version >= 30:
_shadowed_dict = _shadowed_dict_newstyle
_get_type = type
_static_getmro = _static_getmro_newstyle
else:
def _shadowed_dict(klass):
"""
In Python 2 __dict__ is not overwritable:
class Foo(object): pass
setattr(Foo, '__dict__', 4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __dict__ must be a dictionary object
It applies to both newstyle and oldstyle classes:
class Foo(object): pass
setattr(Foo, '__dict__', 4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: attribute '__dict__' of 'type' objects is not writable
It also applies to instances of those objects. However to keep things
straight forward, newstyle classes always use the complicated way of
accessing it while oldstyle classes just use getattr.
"""
if type(klass) is _oldstyle_class_type:
return getattr(klass, '__dict__', _sentinel)
return _shadowed_dict_newstyle(klass)
class _OldStyleClass:
pass
_oldstyle_instance_type = type(_OldStyleClass())
_oldstyle_class_type = type(_OldStyleClass)
def _get_type(obj):
type_ = object.__getattribute__(obj, '__class__')
if type_ is _oldstyle_instance_type:
# Somehow for old style classes we need to access it directly.
return obj.__class__
return type_
def _static_getmro(klass):
if type(klass) is _oldstyle_class_type:
def oldstyle_mro(klass):
"""
Oldstyle mro is a really simplistic way of look up mro:
https://stackoverflow.com/questions/54867/what-is-the-difference-between-old-style-and-new-style-classes-in-python
"""
yield klass
for base in klass.__bases__:
for yield_from in oldstyle_mro(base):
yield yield_from
return oldstyle_mro(klass)
return _static_getmro_newstyle(klass)
def _safe_hasattr(obj, name): def _safe_hasattr(obj, name):
return _check_class(_get_type(obj), name) is not _sentinel return _check_class(type(obj), name) is not _sentinel
def _safe_is_data_descriptor(obj): def _safe_is_data_descriptor(obj):
@@ -151,7 +88,7 @@ def getattr_static(obj, attr, default=_sentinel):
""" """
instance_result = _sentinel instance_result = _sentinel
if not _is_type(obj): if not _is_type(obj):
klass = _get_type(obj) klass = type(obj)
dict_attr = _shadowed_dict(klass) dict_attr = _shadowed_dict(klass)
if (dict_attr is _sentinel or type(dict_attr) is types.MemberDescriptorType): if (dict_attr is _sentinel or type(dict_attr) is types.MemberDescriptorType):
instance_result = _check_instance(obj, attr) instance_result = _check_instance(obj, attr)
+9 -12
View File
@@ -4,11 +4,9 @@ Used only for REPL Completion.
import inspect import inspect
import os import os
import sys
from jedi.parser_utils import get_cached_code_lines from jedi.parser_utils import get_cached_code_lines
from jedi._compatibility import unwrap
from jedi import settings from jedi import settings
from jedi.cache import memoize_method from jedi.cache import memoize_method
from jedi.inference import compiled from jedi.inference import compiled
@@ -44,7 +42,7 @@ class MixedObject(ValueWrapper):
to modify the runtime. to modify the runtime.
""" """
def __init__(self, compiled_value, tree_value): def __init__(self, compiled_value, tree_value):
super(MixedObject, self).__init__(tree_value) super().__init__(tree_value)
self.compiled_value = compiled_value self.compiled_value = compiled_value
self.access_handle = compiled_value.access_handle self.access_handle = compiled_value.access_handle
@@ -115,7 +113,7 @@ class MixedName(NameWrapper):
The ``CompiledName._compiled_value`` is our MixedObject. The ``CompiledName._compiled_value`` is our MixedObject.
""" """
def __init__(self, wrapped_name, parent_tree_value): def __init__(self, wrapped_name, parent_tree_value):
super(MixedName, self).__init__(wrapped_name) super().__init__(wrapped_name)
self._parent_tree_value = parent_tree_value self._parent_tree_value = parent_tree_value
@property @property
@@ -141,12 +139,12 @@ class MixedName(NameWrapper):
class MixedObjectFilter(compiled.CompiledValueFilter): class MixedObjectFilter(compiled.CompiledValueFilter):
def __init__(self, inference_state, compiled_value, tree_value): def __init__(self, inference_state, compiled_value, tree_value):
super(MixedObjectFilter, self).__init__(inference_state, compiled_value) super().__init__(inference_state, compiled_value)
self._tree_value = tree_value self._tree_value = tree_value
def _create_name(self, name): def _create_name(self, name):
return MixedName( return MixedName(
super(MixedObjectFilter, self)._create_name(name), super()._create_name(name),
self._tree_value, self._tree_value,
) )
@@ -163,12 +161,11 @@ def _load_module(inference_state, path):
def _get_object_to_check(python_object): def _get_object_to_check(python_object):
"""Check if inspect.getfile has a chance to find the source.""" """Check if inspect.getfile has a chance to find the source."""
if sys.version_info[0] > 2: try:
try: python_object = inspect.unwrap(python_object)
python_object = unwrap(python_object) except ValueError:
except ValueError: # Can return a ValueError when it wraps around
# Can return a ValueError when it wraps around pass
pass
if (inspect.ismodule(python_object) if (inspect.ismodule(python_object)
or inspect.isclass(python_object) or inspect.isclass(python_object)
+33 -47
View File
@@ -9,19 +9,14 @@ goals:
import os import os
import sys import sys
import queue
import subprocess import subprocess
import socket
import errno
import traceback import traceback
import weakref
from functools import partial from functools import partial
from threading import Thread 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, \ from jedi._compatibility import pickle_dump, pickle_load
pickle_dump, pickle_load, GeneralizedPopen, weakref
from jedi import debug from jedi import debug
from jedi.cache import memoize_method from jedi.cache import memoize_method
from jedi.inference.compiled.subprocess import functions from jedi.inference.compiled.subprocess import functions
@@ -31,11 +26,27 @@ from jedi.api.exceptions import InternalError
_MAIN_PATH = os.path.join(os.path.dirname(__file__), '__main__.py') _MAIN_PATH = os.path.join(os.path.dirname(__file__), '__main__.py')
PICKLE_PROTOCOL = 4
def _enqueue_output(out, queue): class _GeneralizedPopen(subprocess.Popen):
def __init__(self, *args, **kwargs):
if os.name == 'nt':
try:
# Was introduced in Python 3.7.
CREATE_NO_WINDOW = subprocess.CREATE_NO_WINDOW
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().__init__(*args, **kwargs)
def _enqueue_output(out, queue_):
for line in iter(out.readline, b''): for line in iter(out.readline, b''):
queue.put(line) queue_.put(line)
def _add_stderr_to_debug(stderr_queue): def _add_stderr_to_debug(stderr_queue):
@@ -46,7 +57,7 @@ def _add_stderr_to_debug(stderr_queue):
line = stderr_queue.get_nowait() line = stderr_queue.get_nowait()
line = line.decode('utf-8', 'replace') line = line.decode('utf-8', 'replace')
debug.warning('stderr output: %s' % line.rstrip('\n')) debug.warning('stderr output: %s' % line.rstrip('\n'))
except Empty: except queue.Empty:
break break
@@ -105,7 +116,7 @@ class InferenceStateSameProcess(_InferenceStateProcess):
class InferenceStateSubprocess(_InferenceStateProcess): class InferenceStateSubprocess(_InferenceStateProcess):
def __init__(self, inference_state, compiled_subprocess): def __init__(self, inference_state, compiled_subprocess):
super(InferenceStateSubprocess, self).__init__(inference_state) super().__init__(inference_state)
self._used = False self._used = False
self._compiled_subprocess = compiled_subprocess self._compiled_subprocess = compiled_subprocess
@@ -153,8 +164,6 @@ class InferenceStateSubprocess(_InferenceStateProcess):
class CompiledSubprocess(object): class CompiledSubprocess(object):
is_crashed = False is_crashed = False
# Start with 2, gets set after _get_info.
_pickle_protocol = 2
def __init__(self, executable, env_vars=None): def __init__(self, executable, env_vars=None):
self._executable = executable self._executable = executable
@@ -164,10 +173,9 @@ class CompiledSubprocess(object):
def __repr__(self): def __repr__(self):
pid = os.getpid() pid = os.getpid()
return '<%s _executable=%r, _pickle_protocol=%r, is_crashed=%r, pid=%r>' % ( return '<%s _executable=%r, is_crashed=%r, pid=%r>' % (
self.__class__.__name__, self.__class__.__name__,
self._executable, self._executable,
self._pickle_protocol,
self.is_crashed, self.is_crashed,
pid, pid,
) )
@@ -182,17 +190,14 @@ class CompiledSubprocess(object):
os.path.dirname(os.path.dirname(parso_path)), os.path.dirname(os.path.dirname(parso_path)),
'.'.join(str(x) for x in sys.version_info[:3]), '.'.join(str(x) for x in sys.version_info[:3]),
) )
process = GeneralizedPopen( process = _GeneralizedPopen(
args, args,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
# Use system default buffering on Python 2 to improve performance
# (this is already the case on Python 3).
bufsize=-1,
env=self._env_vars env=self._env_vars
) )
self._stderr_queue = Queue() self._stderr_queue = queue.Queue()
self._stderr_thread = t = Thread( self._stderr_thread = t = Thread(
target=_enqueue_output, target=_enqueue_output,
args=(process.stderr, self._stderr_queue) args=(process.stderr, self._stderr_queue)
@@ -231,20 +236,10 @@ class CompiledSubprocess(object):
if self.is_crashed: if self.is_crashed:
raise InternalError("The subprocess %s has crashed." % self._executable) raise InternalError("The subprocess %s has crashed." % self._executable)
if not is_py3:
# Python 2 compatibility
kwargs = {force_unicode(key): value for key, value in kwargs.items()}
data = inference_state_id, function, args, kwargs data = inference_state_id, function, args, kwargs
try: try:
pickle_dump(data, self._get_process().stdin, self._pickle_protocol) pickle_dump(data, self._get_process().stdin, PICKLE_PROTOCOL)
except (socket.error, IOError) as e: except BrokenPipeError:
# Once Python2 will be removed we can just use `BrokenPipeError`.
# Also, somehow in windows it returns EINVAL instead of EPIPE if
# the subprocess dies.
if e.errno not in (errno.EPIPE, errno.EINVAL):
# Not a broken pipe
raise
self._kill() self._kill()
raise InternalError("The subprocess %s was killed. Maybe out of memory?" raise InternalError("The subprocess %s was killed. Maybe out of memory?"
% self._executable) % self._executable)
@@ -286,12 +281,11 @@ class CompiledSubprocess(object):
class Listener(object): class Listener(object):
def __init__(self, pickle_protocol): def __init__(self):
self._inference_states = {} self._inference_states = {}
# TODO refactor so we don't need to process anymore just handle # TODO refactor so we don't need to process anymore just handle
# controlling. # controlling.
self._process = _InferenceStateProcess(Listener) self._process = _InferenceStateProcess(Listener)
self._pickle_protocol = pickle_protocol
def _get_inference_state(self, function, inference_state_id): def _get_inference_state(self, function, inference_state_id):
from jedi.inference import InferenceState from jedi.inference import InferenceState
@@ -334,15 +328,8 @@ class Listener(object):
# because stdout is used for IPC. # because stdout is used for IPC.
sys.stdout = open(os.devnull, 'w') sys.stdout = open(os.devnull, 'w')
stdin = sys.stdin stdin = sys.stdin
if sys.version_info[0] > 2: stdout = stdout.buffer
stdout = stdout.buffer stdin = stdin.buffer
stdin = stdin.buffer
# Python 2 opens streams in text mode on Windows. Set stdout and stdin
# to binary mode.
elif sys.platform == 'win32':
import msvcrt
msvcrt.setmode(stdout.fileno(), os.O_BINARY)
msvcrt.setmode(stdin.fileno(), os.O_BINARY)
while True: while True:
try: try:
@@ -356,7 +343,7 @@ class Listener(object):
except Exception as e: except Exception as e:
result = True, traceback.format_exc(), e result = True, traceback.format_exc(), e
pickle_dump(result, stdout, self._pickle_protocol) pickle_dump(result, stdout, PICKLE_PROTOCOL)
class AccessHandle(object): class AccessHandle(object):
@@ -385,9 +372,8 @@ class AccessHandle(object):
if name in ('id', 'access') or name.startswith('_'): if name in ('id', 'access') or name.startswith('_'):
raise AttributeError("Something went wrong with unpickling") raise AttributeError("Something went wrong with unpickling")
# if not is_py3: print >> sys.stderr, name
# print('getattr', name, file=sys.stderr) # print('getattr', name, file=sys.stderr)
return partial(self._workaround, force_unicode(name)) return partial(self._workaround, name)
def _workaround(self, name, *args, **kwargs): def _workaround(self, name, *args, **kwargs):
""" """
+19 -35
View File
@@ -1,5 +1,10 @@
import os import os
import sys import sys
from importlib.machinery import PathFinder
# Remove the first entry, because it's simply a directory entry that equals
# this directory.
del sys.path[0]
def _get_paths(): def _get_paths():
@@ -11,45 +16,24 @@ def _get_paths():
return {'jedi': _jedi_path, 'parso': _parso_path} return {'jedi': _jedi_path, 'parso': _parso_path}
# Remove the first entry, because it's simply a directory entry that equals class _ExactImporter(object):
# this directory. def __init__(self, path_dct):
del sys.path[0] self._path_dct = path_dct
if sys.version_info > (3, 4): def find_module(self, fullname, path=None):
from importlib.machinery import PathFinder if path is None and fullname in self._path_dct:
p = self._path_dct[fullname]
loader = PathFinder.find_module(fullname, path=[p])
return loader
return None
class _ExactImporter(object):
def __init__(self, path_dct):
self._path_dct = path_dct
def find_module(self, fullname, path=None):
if path is None and fullname in self._path_dct:
p = self._path_dct[fullname]
loader = PathFinder.find_module(fullname, path=[p])
return loader
return None
# Try to import jedi/parso.
sys.meta_path.insert(0, _ExactImporter(_get_paths()))
from jedi.inference.compiled import subprocess # NOQA
sys.meta_path.pop(0)
else:
import imp
def load(name):
paths = list(_get_paths().values())
fp, pathname, description = imp.find_module(name, paths)
return imp.load_module(name, fp, pathname, description)
load('parso')
load('jedi')
from jedi.inference.compiled import subprocess # NOQA
from jedi._compatibility import highest_pickle_protocol # noqa: E402
# Try to import jedi/parso.
sys.meta_path.insert(0, _ExactImporter(_get_paths()))
from jedi.inference.compiled import subprocess # noqa: E402
sys.meta_path.pop(0)
# Retrieve the pickle protocol. # Retrieve the pickle protocol.
host_sys_version = [int(x) for x in sys.argv[2].split('.')] host_sys_version = [int(x) for x in sys.argv[2].split('.')]
pickle_protocol = highest_pickle_protocol([sys.version_info, host_sys_version])
# And finally start the client. # And finally start the client.
subprocess.Listener(pickle_protocol=pickle_protocol).listen() subprocess.Listener().listen()
+131 -10
View File
@@ -1,14 +1,16 @@
from __future__ import print_function
import sys import sys
import os import os
import re
import inspect import inspect
import importlib
import warnings
from zipimport import zipimporter
from importlib.machinery import all_suffixes
from jedi._compatibility import find_module, cast_path, force_unicode, \ from jedi._compatibility import cast_path
all_suffixes, scandir
from jedi.inference.compiled import access from jedi.inference.compiled import access
from jedi import debug from jedi import debug
from jedi import parser_utils from jedi import parser_utils
from jedi.file_io import KnownContentFileIO, ZipFileIO
def get_sys_path(): def get_sys_path():
@@ -35,7 +37,7 @@ def get_module_info(inference_state, sys_path=None, full_name=None, **kwargs):
if sys_path is not None: if sys_path is not None:
sys.path, temp = sys_path, sys.path sys.path, temp = sys_path, sys.path
try: try:
return find_module(full_name=full_name, **kwargs) return _find_module(full_name=full_name, **kwargs)
except ImportError: except ImportError:
return None, None return None, None
finally: finally:
@@ -44,7 +46,7 @@ def get_module_info(inference_state, sys_path=None, full_name=None, **kwargs):
def get_builtin_module_names(inference_state): def get_builtin_module_names(inference_state):
return list(map(force_unicode, sys.builtin_module_names)) return sys.builtin_module_names
def _test_raise_error(inference_state, exception_type): def _test_raise_error(inference_state, exception_type):
@@ -90,7 +92,7 @@ def _iter_module_names(inference_state, paths):
# Python modules/packages # Python modules/packages
for path in paths: for path in paths:
try: try:
dirs = scandir(path) dirs = os.scandir(path)
except OSError: except OSError:
# The file might not exist or reading it might lead to an error. # The file might not exist or reading it might lead to an error.
debug.warning("Not possible to list directory: %s", path) debug.warning("Not possible to list directory: %s", path)
@@ -99,10 +101,9 @@ def _iter_module_names(inference_state, paths):
name = dir_entry.name name = dir_entry.name
# First Namespaces then modules/stubs # First Namespaces then modules/stubs
if dir_entry.is_dir(): if dir_entry.is_dir():
# pycache is obviously not an interestin namespace. Also the # pycache is obviously not an interesting namespace. Also the
# name must be a valid identifier. # name must be a valid identifier.
# TODO use str.isidentifier, once Python 2 is removed if name != '__pycache__' and name.isidentifier():
if name != '__pycache__' and not re.search(r'\W|^\d', name):
yield name yield name
else: else:
if name.endswith('.pyi'): # Stub files if name.endswith('.pyi'): # Stub files
@@ -113,3 +114,123 @@ def _iter_module_names(inference_state, paths):
if modname and '.' not in modname: if modname and '.' not in modname:
if modname != '__init__': if modname != '__init__':
yield modname yield modname
def _find_module(string, path=None, full_name=None, is_global_search=True):
"""
Provides information about a module.
This function isolates the differences in importing libraries introduced with
python 3.3 on; it gets a module name and optionally a path. It will return a
tuple containin an open file for the module (if not builtin), the filename
or the name of the module if it is a builtin one and a boolean indicating
if the module is contained in a package.
"""
spec = None
loader = None
for finder in sys.meta_path:
if is_global_search and finder != importlib.machinery.PathFinder:
p = None
else:
p = path
try:
find_spec = finder.find_spec
except AttributeError:
# These are old-school clases that still have a different API, just
# ignore those.
continue
spec = find_spec(string, p)
if spec is not None:
loader = spec.loader
if loader is None and not spec.has_location:
# This is a namespace package.
full_name = string if not path else full_name
implicit_ns_info = ImplicitNSInfo(full_name, spec.submodule_search_locations._path)
return implicit_ns_info, True
break
return _find_module_py33(string, path, loader)
def _find_module_py33(string, path=None, loader=None, full_name=None, is_global_search=True):
loader = loader or importlib.machinery.PathFinder.find_module(string, path)
if loader is None and path is None: # Fallback to find builtins
try:
with warnings.catch_warnings(record=True):
# Mute "DeprecationWarning: Use importlib.util.find_spec()
# instead." While we should replace that in the future, it's
# probably good to wait until we deprecate Python 3.3, since
# it was added in Python 3.4 and find_loader hasn't been
# removed in 3.6.
loader = importlib.find_loader(string)
except ValueError as e:
# See #491. Importlib might raise a ValueError, to avoid this, we
# just raise an ImportError to fix the issue.
raise ImportError("Originally " + repr(e))
if loader is None:
raise ImportError("Couldn't find a loader for {}".format(string))
return _from_loader(loader, string)
def _from_loader(loader, string):
try:
is_package_method = loader.is_package
except AttributeError:
is_package = False
else:
is_package = is_package_method(string)
try:
get_filename = loader.get_filename
except AttributeError:
return None, is_package
else:
module_path = cast_path(get_filename(string))
# To avoid unicode and read bytes, "overwrite" loader.get_source if
# possible.
try:
f = type(loader).get_source
except AttributeError:
raise ImportError("get_source was not defined on loader")
if f is not importlib.machinery.SourceFileLoader.get_source:
# Unfortunately we are reading unicode here, not bytes.
# It seems hard to get bytes, because the zip importer
# logic just unpacks the zip file and returns a file descriptor
# that we cannot as easily access. Therefore we just read it as
# a string in the cases where get_source was overwritten.
code = loader.get_source(string)
else:
code = _get_source(loader, string)
if code is None:
return None, is_package
if isinstance(loader, zipimporter):
return ZipFileIO(module_path, code, cast_path(loader.archive)), is_package
return KnownContentFileIO(module_path, code), is_package
def _get_source(loader, fullname):
"""
This method is here as a replacement for SourceLoader.get_source. That
method returns unicode, but we prefer bytes.
"""
path = loader.get_filename(fullname)
try:
return loader.get_data(path)
except OSError:
raise ImportError('source not available through get_data()',
name=fullname)
class ImplicitNSInfo(object):
"""Stores information returned from an implicit namespace spec"""
def __init__(self, name, paths):
self.name = name
self.paths = paths
+24 -27
View File
@@ -3,10 +3,12 @@ Imitate the parser representation.
""" """
import re import re
from functools import partial from functools import partial
from inspect import Parameter
from pathlib import Path
from jedi import debug from jedi import debug
from jedi.inference.utils import to_list from jedi.inference.utils import to_list
from jedi._compatibility import force_unicode, Parameter, cast_path from jedi._compatibility import cast_path
from jedi.cache import memoize_method from jedi.cache import memoize_method
from jedi.inference.filters import AbstractFilter from jedi.inference.filters import AbstractFilter
from jedi.inference.names import AbstractNameDefinition, ValueNameMixin, \ from jedi.inference.names import AbstractNameDefinition, ValueNameMixin, \
@@ -29,7 +31,7 @@ class CheckAttribute(object):
def __call__(self, func): def __call__(self, func):
self.func = func self.func = func
if self.check_name is None: if self.check_name is None:
self.check_name = force_unicode(func.__name__[2:]) self.check_name = func.__name__[2:]
return self return self
def __get__(self, instance, owner): def __get__(self, instance, owner):
@@ -43,7 +45,7 @@ class CheckAttribute(object):
class CompiledValue(Value): class CompiledValue(Value):
def __init__(self, inference_state, access_handle, parent_context=None): def __init__(self, inference_state, access_handle, parent_context=None):
super(CompiledValue, self).__init__(inference_state, parent_context) super().__init__(inference_state, parent_context)
self.access_handle = access_handle self.access_handle = access_handle
def py__call__(self, arguments): def py__call__(self, arguments):
@@ -56,9 +58,9 @@ class CompiledValue(Value):
).execute_annotation() ).execute_annotation()
try: try:
self.access_handle.getattr_paths(u'__call__') self.access_handle.getattr_paths('__call__')
except AttributeError: except AttributeError:
return super(CompiledValue, self).py__call__(arguments) return super().py__call__(arguments)
else: else:
if self.access_handle.is_class(): if self.access_handle.is_class():
from jedi.inference.value import CompiledInstance from jedi.inference.value import CompiledInstance
@@ -163,7 +165,7 @@ class CompiledValue(Value):
try: try:
access = self.access_handle.py__simple_getitem__(index) access = self.access_handle.py__simple_getitem__(index)
except AttributeError: except AttributeError:
return super(CompiledValue, self).py__simple_getitem__(index) return super().py__simple_getitem__(index)
if access is None: if access is None:
return NO_VALUES return NO_VALUES
@@ -174,19 +176,15 @@ class CompiledValue(Value):
if all_access_paths is None: if all_access_paths is None:
# This means basically that no __getitem__ has been defined on this # This means basically that no __getitem__ has been defined on this
# object. # object.
return super(CompiledValue, self).py__getitem__(index_value_set, contextualized_node) return super().py__getitem__(index_value_set, contextualized_node)
return ValueSet( return ValueSet(
create_from_access_path(self.inference_state, access) create_from_access_path(self.inference_state, access)
for access in all_access_paths for access in all_access_paths
) )
def py__iter__(self, contextualized_node=None): def py__iter__(self, contextualized_node=None):
# Python iterators are a bit strange, because there's no need for
# the __iter__ function as long as __getitem__ is defined (it will
# just start with __getitem__(0). This is especially true for
# Python 2 strings, where `str.__iter__` is not even defined.
if not self.access_handle.has_iter(): if not self.access_handle.has_iter():
for x in super(CompiledValue, self).py__iter__(contextualized_node): for x in super().py__iter__(contextualized_node):
yield x yield x
access_path_list = self.access_handle.py__iter__list() access_path_list = self.access_handle.py__iter__list()
@@ -264,7 +262,7 @@ class CompiledValue(Value):
v.with_generics(arguments) v.with_generics(arguments)
for v in self.inference_state.typing_module.py__getattribute__(name) for v in self.inference_state.typing_module.py__getattribute__(name)
]).execute_annotation() ]).execute_annotation()
return super(CompiledValue, self).execute_annotation() return super().execute_annotation()
def negate(self): def negate(self):
return create_from_access_path(self.inference_state, self.access_handle.negate()) return create_from_access_path(self.inference_state, self.access_handle.negate())
@@ -315,7 +313,10 @@ class CompiledModule(CompiledValue):
return tuple(name.split('.')) return tuple(name.split('.'))
def py__file__(self): def py__file__(self):
return cast_path(self.access_handle.py__file__()) path = cast_path(self.access_handle.py__file__())
if path is None:
return None
return Path(path)
class CompiledName(AbstractNameDefinition): class CompiledName(AbstractNameDefinition):
@@ -456,9 +457,6 @@ class CompiledValueFilter(AbstractFilter):
""" """
To remove quite a few access calls we introduced the callback here. To remove quite a few access calls we introduced the callback here.
""" """
# Always use unicode objects in Python 2 from here.
name = force_unicode(name)
if self._inference_state.allow_descriptor_getattr: if self._inference_state.allow_descriptor_getattr:
pass pass
@@ -502,7 +500,7 @@ class CompiledValueFilter(AbstractFilter):
# ``dir`` doesn't include the type names. # ``dir`` doesn't include the type names.
if not self.is_instance and needs_type_completions: if not self.is_instance and needs_type_completions:
for filter in builtin_from_name(self._inference_state, u'type').get_filters(): for filter in builtin_from_name(self._inference_state, 'type').get_filters():
names += filter.values() names += filter.values()
return names return names
@@ -518,11 +516,11 @@ class CompiledValueFilter(AbstractFilter):
docstr_defaults = { docstr_defaults = {
'floating point number': u'float', 'floating point number': 'float',
'character': u'str', 'character': 'str',
'integer': u'int', 'integer': 'int',
'dictionary': u'dict', 'dictionary': 'dict',
'string': u'str', 'string': 'str',
} }
@@ -534,7 +532,6 @@ def _parse_function_doc(doc):
TODO docstrings like utime(path, (atime, mtime)) and a(b [, b]) -> None TODO docstrings like utime(path, (atime, mtime)) and a(b [, b]) -> None
TODO docstrings like 'tuple of integers' TODO docstrings like 'tuple of integers'
""" """
doc = force_unicode(doc)
# parse round parentheses: def func(a, (b,c)) # parse round parentheses: def func(a, (b,c))
try: try:
count = 0 count = 0
@@ -553,7 +550,7 @@ def _parse_function_doc(doc):
# UnboundLocalError for undefined end in last line # UnboundLocalError for undefined end in last line
debug.dbg('no brackets found - no param') debug.dbg('no brackets found - no param')
end = 0 end = 0
param_str = u'' param_str = ''
else: else:
# remove square brackets, that show an optional param ( = None) # remove square brackets, that show an optional param ( = None)
def change_options(m): def change_options(m):
@@ -571,9 +568,9 @@ def _parse_function_doc(doc):
param_str = param_str.replace('-', '_') # see: isinstance.__doc__ param_str = param_str.replace('-', '_') # see: isinstance.__doc__
# parse return value # parse return value
r = re.search(u'-[>-]* ', doc[end:end + 7]) r = re.search('-[>-]* ', doc[end:end + 7])
if r is None: if r is None:
ret = u'' ret = ''
else: else:
index = end + r.end() index = end + r.end()
# get result type, which can contain newlines # get result type, which can contain newlines
+6 -8
View File
@@ -164,7 +164,7 @@ class ValueContext(AbstractContext):
Should be defined, otherwise the API returns empty types. Should be defined, otherwise the API returns empty types.
""" """
def __init__(self, value): def __init__(self, value):
super(ValueContext, self).__init__(value.inference_state) super().__init__(value.inference_state)
self._value = value self._value = value
@property @property
@@ -323,8 +323,7 @@ class ModuleContext(TreeContextMixin, ValueContext):
), ),
self.get_global_filter(), self.get_global_filter(),
) )
for f in filters: # Python 2... yield from filters
yield f
def get_global_filter(self): def get_global_filter(self):
return GlobalNameFilter(self, self.tree_node) return GlobalNameFilter(self, self.tree_node)
@@ -375,7 +374,7 @@ class ClassContext(TreeContextMixin, ValueContext):
class CompForContext(TreeContextMixin, AbstractContext): class CompForContext(TreeContextMixin, AbstractContext):
def __init__(self, parent_context, comp_for): def __init__(self, parent_context, comp_for):
super(CompForContext, self).__init__(parent_context.inference_state) super().__init__(parent_context.inference_state)
self.tree_node = comp_for self.tree_node = comp_for
self.parent_context = parent_context self.parent_context = parent_context
@@ -439,13 +438,12 @@ def get_global_filters(context, until_position, origin_scope):
For global name lookups. The filters will handle name resolution For global name lookups. The filters will handle name resolution
themselves, but here we gather possible filters downwards. themselves, but here we gather possible filters downwards.
>>> from jedi._compatibility import u, no_unicode_pprint
>>> from jedi import Script >>> from jedi import Script
>>> script = Script(u(''' >>> script = Script('''
... x = ['a', 'b', 'c'] ... x = ['a', 'b', 'c']
... def func(): ... def func():
... y = None ... y = None
... ''')) ... ''')
>>> module_node = script._module_node >>> module_node = script._module_node
>>> scope = next(module_node.iter_funcdefs()) >>> scope = next(module_node.iter_funcdefs())
>>> scope >>> scope
@@ -455,7 +453,7 @@ def get_global_filters(context, until_position, origin_scope):
First we get the names from the function scope. First we get the names from the function scope.
>>> no_unicode_pprint(filters[0]) # doctest: +ELLIPSIS >>> print(filters[0]) # doctest: +ELLIPSIS
MergedFilter(<ParserTreeFilter: ...>, <GlobalNameFilter: ...>) MergedFilter(<ParserTreeFilter: ...>, <GlobalNameFilter: ...>)
>>> sorted(str(n) for n in filters[0].values()) # doctest: +NORMALIZE_WHITESPACE >>> sorted(str(n) for n in filters[0].values()) # doctest: +NORMALIZE_WHITESPACE
['<TreeNameDefinition: string_name=func start_pos=(3, 4)>', ['<TreeNameDefinition: string_name=func start_pos=(3, 4)>',
+3 -7
View File
@@ -21,7 +21,6 @@ from textwrap import dedent
from parso import parse, ParserSyntaxError from parso import parse, ParserSyntaxError
from jedi._compatibility import u
from jedi import debug from jedi import debug
from jedi.common import indent_block from jedi.common import indent_block
from jedi.inference.cache import inference_state_method_cache from jedi.inference.cache import inference_state_method_cache
@@ -184,7 +183,7 @@ def _strip_rst_role(type_str):
def _infer_for_statement_string(module_context, string): def _infer_for_statement_string(module_context, string):
code = dedent(u(""" code = dedent("""
def pseudo_docstring_stuff(): def pseudo_docstring_stuff():
''' '''
Create a pseudo function for docstring statements. Create a pseudo function for docstring statements.
@@ -192,7 +191,7 @@ def _infer_for_statement_string(module_context, string):
is still a function. is still a function.
''' '''
{} {}
""")) """)
if string is None: if string is None:
return [] return []
@@ -201,11 +200,8 @@ def _infer_for_statement_string(module_context, string):
# (e.g., 'threading' in 'threading.Thread'). # (e.g., 'threading' in 'threading.Thread').
string = 'import %s\n' % element + string string = 'import %s\n' % element + string
# Take the default grammar here, if we load the Python 2.7 grammar here, it
# will be impossible to use `...` (Ellipsis) as a token. Docstring types
# don't need to conform with the current grammar.
debug.dbg('Parse docstring code %s', string, color='BLUE') debug.dbg('Parse docstring code %s', string, color='BLUE')
grammar = module_context.inference_state.latest_grammar grammar = module_context.inference_state.grammar
try: try:
module = grammar.parse(code.format(indent_block(string)), error_recovery=False) module = grammar.parse(code.format(indent_block(string)), error_recovery=False)
except ParserSyntaxError: except ParserSyntaxError:
+18 -24
View File
@@ -7,7 +7,6 @@ import weakref
from parso.tree import search_ancestor from parso.tree import search_ancestor
from jedi._compatibility import use_metaclass
from jedi.inference import flow_analysis from jedi.inference import flow_analysis
from jedi.inference.base_value import ValueSet, ValueWrapper, \ from jedi.inference.base_value import ValueSet, ValueWrapper, \
LazyValueWrapper LazyValueWrapper
@@ -109,13 +108,13 @@ class ParserTreeFilter(AbstractUsedNamesFilter):
""" """
if node_context is None: if node_context is None:
node_context = parent_context node_context = parent_context
super(ParserTreeFilter, self).__init__(parent_context, node_context.tree_node) super().__init__(parent_context, node_context.tree_node)
self._node_context = node_context self._node_context = node_context
self._origin_scope = origin_scope self._origin_scope = origin_scope
self._until_position = until_position self._until_position = until_position
def _filter(self, names): def _filter(self, names):
names = super(ParserTreeFilter, self)._filter(names) names = super()._filter(names)
names = [n for n in names if self._is_name_reachable(n)] names = [n for n in names if self._is_name_reachable(n)]
return list(self._check_flows(names)) return list(self._check_flows(names))
@@ -143,7 +142,7 @@ class ParserTreeFilter(AbstractUsedNamesFilter):
class _FunctionExecutionFilter(ParserTreeFilter): class _FunctionExecutionFilter(ParserTreeFilter):
def __init__(self, parent_context, function_value, until_position, origin_scope): def __init__(self, parent_context, function_value, until_position, origin_scope):
super(_FunctionExecutionFilter, self).__init__( super().__init__(
parent_context, parent_context,
until_position=until_position, until_position=until_position,
origin_scope=origin_scope, origin_scope=origin_scope,
@@ -167,9 +166,9 @@ class _FunctionExecutionFilter(ParserTreeFilter):
class FunctionExecutionFilter(_FunctionExecutionFilter): class FunctionExecutionFilter(_FunctionExecutionFilter):
def __init__(self, *args, **kwargs): def __init__(self, *args, arguments, **kwargs):
self._arguments = kwargs.pop('arguments') # Python 2 super().__init__(*args, **kwargs)
super(FunctionExecutionFilter, self).__init__(*args, **kwargs) self._arguments = arguments
def _convert_param(self, param, name): def _convert_param(self, param, name):
return ParamName(self._function_value, name, self._arguments) return ParamName(self._function_value, name, self._arguments)
@@ -246,10 +245,10 @@ class MergedFilter(object):
class _BuiltinMappedMethod(ValueWrapper): class _BuiltinMappedMethod(ValueWrapper):
"""``Generator.__next__`` ``dict.values`` methods and so on.""" """``Generator.__next__`` ``dict.values`` methods and so on."""
api_type = u'function' api_type = 'function'
def __init__(self, value, method, builtin_func): def __init__(self, value, method, builtin_func):
super(_BuiltinMappedMethod, self).__init__(builtin_func) super().__init__(builtin_func)
self._value = value self._value = value
self._method = method self._method = method
@@ -264,14 +263,9 @@ class SpecialMethodFilter(DictFilter):
classes like Generator (for __next__, etc). classes like Generator (for __next__, etc).
""" """
class SpecialMethodName(AbstractNameDefinition): class SpecialMethodName(AbstractNameDefinition):
api_type = u'function' api_type = 'function'
def __init__(self, parent_context, string_name, value, builtin_value):
callable_, python_version = value
if python_version is not None and \
python_version != parent_context.inference_state.environment.version_info.major:
raise KeyError
def __init__(self, parent_context, string_name, callable_, builtin_value):
self.parent_context = parent_context self.parent_context = parent_context
self.string_name = string_name self.string_name = string_name
self._callable = callable_ self._callable = callable_
@@ -293,7 +287,7 @@ class SpecialMethodFilter(DictFilter):
]) ])
def __init__(self, value, dct, builtin_value): def __init__(self, value, dct, builtin_value):
super(SpecialMethodFilter, self).__init__(dct) super().__init__(dct)
self.value = value self.value = value
self._builtin_value = builtin_value self._builtin_value = builtin_value
""" """
@@ -309,7 +303,7 @@ class SpecialMethodFilter(DictFilter):
class _OverwriteMeta(type): class _OverwriteMeta(type):
def __init__(cls, name, bases, dct): def __init__(cls, name, bases, dct):
super(_OverwriteMeta, cls).__init__(name, bases, dct) super().__init__(name, bases, dct)
base_dct = {} base_dct = {}
for base_cls in reversed(cls.__bases__): for base_cls in reversed(cls.__bases__):
@@ -334,20 +328,20 @@ class _AttributeOverwriteMixin(object):
yield filter yield filter
class LazyAttributeOverwrite(use_metaclass(_OverwriteMeta, _AttributeOverwriteMixin, class LazyAttributeOverwrite(_AttributeOverwriteMixin, LazyValueWrapper,
LazyValueWrapper)): metaclass=_OverwriteMeta):
def __init__(self, inference_state): def __init__(self, inference_state):
self.inference_state = inference_state self.inference_state = inference_state
class AttributeOverwrite(use_metaclass(_OverwriteMeta, _AttributeOverwriteMixin, class AttributeOverwrite(_AttributeOverwriteMixin, ValueWrapper,
ValueWrapper)): metaclass=_OverwriteMeta):
pass pass
def publish_method(method_name, python_version_match=None): def publish_method(method_name):
def decorator(func): def decorator(func):
dct = func.__dict__.setdefault('registered_overwritten_methods', {}) dct = func.__dict__.setdefault('registered_overwritten_methods', {})
dct[method_name] = func, python_version_match dct[method_name] = func
return func return func
return decorator return decorator
+6 -6
View File
@@ -6,10 +6,10 @@ as annotations in future python versions.
""" """
import re import re
from inspect import Parameter
from parso import ParserSyntaxError, parse from parso import ParserSyntaxError, parse
from jedi._compatibility import force_unicode, Parameter
from jedi.inference.cache import inference_state_method_cache from jedi.inference.cache import inference_state_method_cache
from jedi.inference.base_value import ValueSet, NO_VALUES from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference.gradual.base import DefineGenericBaseClass, GenericClass from jedi.inference.gradual.base import DefineGenericBaseClass, GenericClass
@@ -53,7 +53,7 @@ def _infer_annotation_string(context, string, index=None):
value_set = context.infer_node(node) value_set = context.infer_node(node)
if index is not None: if index is not None:
value_set = value_set.filter( value_set = value_set.filter(
lambda value: value.array_type == u'tuple' # noqa lambda value: value.array_type == 'tuple' # noqa
and len(list(value.py__iter__())) >= index and len(list(value.py__iter__())) >= index
).py__simple_getitem__(index) ).py__simple_getitem__(index)
return value_set return value_set
@@ -62,7 +62,7 @@ def _infer_annotation_string(context, string, index=None):
def _get_forward_reference_node(context, string): def _get_forward_reference_node(context, string):
try: try:
new_node = context.inference_state.grammar.parse( new_node = context.inference_state.grammar.parse(
force_unicode(string), string,
start_symbol='eval_input', start_symbol='eval_input',
error_recovery=False error_recovery=False
) )
@@ -138,8 +138,7 @@ def _infer_param(function_value, param):
""" """
annotation = param.annotation annotation = param.annotation
if annotation is None: if annotation is None:
# If no Python 3-style annotation, look for a Python 2-style comment # If no Python 3-style annotation, look for a comment annotation.
# annotation.
# Identify parameters to function in the same sequence as they would # Identify parameters to function in the same sequence as they would
# appear in a type comment. # appear in a type comment.
all_params = [child for child in param.parent.children all_params = [child for child in param.parent.children
@@ -204,7 +203,8 @@ def infer_return_types(function, arguments):
all_annotations = py__annotations__(function.tree_node) all_annotations = py__annotations__(function.tree_node)
annotation = all_annotations.get("return", None) annotation = all_annotations.get("return", None)
if annotation is None: if annotation is None:
# If there is no Python 3-type annotation, look for a Python 2-type annotation # If there is no Python 3-type annotation, look for an annotation
# comment.
node = function.tree_node node = function.tree_node
comment = parser_utils.get_following_comment_same_line(node) comment = parser_utils.get_following_comment_same_line(node)
if comment is None: if comment is None:
+11 -11
View File
@@ -70,7 +70,7 @@ class _TypeVarFilter(object):
class _AnnotatedClassContext(ClassContext): class _AnnotatedClassContext(ClassContext):
def get_filters(self, *args, **kwargs): def get_filters(self, *args, **kwargs):
filters = super(_AnnotatedClassContext, self).get_filters( filters = super().get_filters(
*args, **kwargs *args, **kwargs
) )
for f in filters: for f in filters:
@@ -164,7 +164,7 @@ class GenericClass(DefineGenericBaseClass, ClassMixin):
my_foo_int_cls = Foo[int] my_foo_int_cls = Foo[int]
""" """
def __init__(self, class_value, generics_manager): def __init__(self, class_value, generics_manager):
super(GenericClass, self).__init__(generics_manager) super().__init__(generics_manager)
self._class_value = class_value self._class_value = class_value
def _get_wrapped_value(self): def _get_wrapped_value(self):
@@ -186,7 +186,7 @@ class GenericClass(DefineGenericBaseClass, ClassMixin):
return _TypeVarFilter(self.get_generics(), self.list_type_vars()) return _TypeVarFilter(self.get_generics(), self.list_type_vars())
def py__call__(self, arguments): def py__call__(self, arguments):
instance, = super(GenericClass, self).py__call__(arguments) instance, = super().py__call__(arguments)
return ValueSet([_GenericInstanceWrapper(instance)]) return ValueSet([_GenericInstanceWrapper(instance)])
def _as_context(self): def _as_context(self):
@@ -201,7 +201,7 @@ class GenericClass(DefineGenericBaseClass, ClassMixin):
return GenericClass(self._class_value, generics_manager) return GenericClass(self._class_value, generics_manager)
def is_sub_class_of(self, class_value): def is_sub_class_of(self, class_value):
if super(GenericClass, self).is_sub_class_of(class_value): if super().is_sub_class_of(class_value):
return True return True
return self._class_value.is_sub_class_of(class_value) return self._class_value.is_sub_class_of(class_value)
@@ -230,7 +230,7 @@ class GenericClass(DefineGenericBaseClass, ClassMixin):
else: else:
continue continue
if py_class.api_type != u'class': if py_class.api_type != 'class':
# Functions & modules don't have an MRO and we're not # Functions & modules don't have an MRO and we're not
# expecting a Callable (those are handled separately within # expecting a Callable (those are handled separately within
# TypingClassValueWithIndex). # TypingClassValueWithIndex).
@@ -309,7 +309,7 @@ class _GenericInstanceWrapper(ValueWrapper):
except IndexError: except IndexError:
pass pass
elif cls.py__name__() == 'Iterator': elif cls.py__name__() == 'Iterator':
return ValueSet([builtin_from_name(self.inference_state, u'None')]) return ValueSet([builtin_from_name(self.inference_state, 'None')])
return self._wrapped_value.py__stop_iteration_returns() return self._wrapped_value.py__stop_iteration_returns()
def get_type_hint(self, add_class_info=True): def get_type_hint(self, add_class_info=True):
@@ -326,10 +326,10 @@ class _PseudoTreeNameClass(Value):
this class. Essentially this class makes it possible to goto that `Tuple` this class. Essentially this class makes it possible to goto that `Tuple`
name, without affecting anything else negatively. name, without affecting anything else negatively.
""" """
api_type = u'class' api_type = 'class'
def __init__(self, parent_context, tree_name): def __init__(self, parent_context, tree_name):
super(_PseudoTreeNameClass, self).__init__( super().__init__(
parent_context.inference_state, parent_context.inference_state,
parent_context parent_context
) )
@@ -356,7 +356,7 @@ class _PseudoTreeNameClass(Value):
def py__class__(self): def py__class__(self):
# This might not be 100% correct, but it is good enough. The details of # This might not be 100% correct, but it is good enough. The details of
# the typing library are not really an issue for Jedi. # the typing library are not really an issue for Jedi.
return builtin_from_name(self.inference_state, u'type') return builtin_from_name(self.inference_state, 'type')
@property @property
def name(self): def name(self):
@@ -388,7 +388,7 @@ class BaseTypingValue(LazyValueWrapper):
class BaseTypingClassWithGenerics(DefineGenericBaseClass): class BaseTypingClassWithGenerics(DefineGenericBaseClass):
def __init__(self, parent_context, tree_name, generics_manager): def __init__(self, parent_context, tree_name, generics_manager):
super(BaseTypingClassWithGenerics, self).__init__(generics_manager) super().__init__(generics_manager)
self.inference_state = parent_context.inference_state self.inference_state = parent_context.inference_state
self.parent_context = parent_context self.parent_context = parent_context
self._tree_name = tree_name self._tree_name = tree_name
@@ -423,7 +423,7 @@ class BaseTypingInstance(LazyValueWrapper):
return ValueName(self, self._tree_name) return ValueName(self, self._tree_name)
def _get_wrapped_value(self): def _get_wrapped_value(self):
object_, = builtin_from_name(self.inference_state, u'object').execute_annotation() object_, = builtin_from_name(self.inference_state, 'object').execute_annotation()
return object_ return object_
def __repr__(self): def __repr__(self):
+7 -7
View File
@@ -10,7 +10,7 @@ class StubModuleValue(ModuleValue):
_module_name_class = StubModuleName _module_name_class = StubModuleName
def __init__(self, non_stub_value_set, *args, **kwargs): def __init__(self, non_stub_value_set, *args, **kwargs):
super(StubModuleValue, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.non_stub_value_set = non_stub_value_set self.non_stub_value_set = non_stub_value_set
def is_stub(self): def is_stub(self):
@@ -30,7 +30,7 @@ class StubModuleValue(ModuleValue):
pass pass
else: else:
names.update(method()) names.update(method())
names.update(super(StubModuleValue, self).sub_modules_dict()) names.update(super().sub_modules_dict())
return names return names
def _get_stub_filters(self, origin_scope): def _get_stub_filters(self, origin_scope):
@@ -40,7 +40,7 @@ class StubModuleValue(ModuleValue):
)] + list(self.iter_star_filters()) )] + list(self.iter_star_filters())
def get_filters(self, origin_scope=None): def get_filters(self, origin_scope=None):
filters = super(StubModuleValue, self).get_filters(origin_scope) filters = super().get_filters(origin_scope)
next(filters, None) # Ignore the first filter and replace it with our own next(filters, None) # Ignore the first filter and replace it with our own
stub_filters = self._get_stub_filters(origin_scope=origin_scope) stub_filters = self._get_stub_filters(origin_scope=origin_scope)
for f in stub_filters: for f in stub_filters:
@@ -57,12 +57,12 @@ class StubModuleContext(ModuleContext):
def get_filters(self, until_position=None, origin_scope=None): def get_filters(self, until_position=None, origin_scope=None):
# Make sure to ignore the position, because positions are not relevant # Make sure to ignore the position, because positions are not relevant
# for stubs. # for stubs.
return super(StubModuleContext, self).get_filters(origin_scope=origin_scope) return super().get_filters(origin_scope=origin_scope)
class TypingModuleWrapper(StubModuleValue): class TypingModuleWrapper(StubModuleValue):
def get_filters(self, *args, **kwargs): def get_filters(self, *args, **kwargs):
filters = super(TypingModuleWrapper, self).get_filters(*args, **kwargs) filters = super().get_filters(*args, **kwargs)
f = next(filters, None) f = next(filters, None)
assert f is not None assert f is not None
yield TypingModuleFilterWrapper(f) yield TypingModuleFilterWrapper(f)
@@ -75,7 +75,7 @@ class TypingModuleWrapper(StubModuleValue):
class TypingModuleContext(ModuleContext): class TypingModuleContext(ModuleContext):
def get_filters(self, *args, **kwargs): def get_filters(self, *args, **kwargs):
filters = super(TypingModuleContext, self).get_filters(*args, **kwargs) filters = super().get_filters(*args, **kwargs)
yield TypingModuleFilterWrapper(next(filters, None)) yield TypingModuleFilterWrapper(next(filters, None))
for f in filters: for f in filters:
yield f yield f
@@ -85,7 +85,7 @@ class StubFilter(ParserTreeFilter):
name_class = StubName name_class = StubName
def _is_name_reachable(self, name): def _is_name_reachable(self, name):
if not super(StubFilter, self)._is_name_reachable(name): if not super()._is_name_reachable(name):
return False return False
# Imports in stub files are only public if they have an "as" # Imports in stub files are only public if they have an "as"
+3 -7
View File
@@ -1,4 +1,3 @@
from jedi._compatibility import unicode, force_unicode
from jedi import debug from jedi import debug
from jedi.inference.base_value import ValueSet, NO_VALUES, ValueWrapper from jedi.inference.base_value import ValueSet, NO_VALUES, ValueWrapper
from jedi.inference.gradual.base import BaseTypingValue from jedi.inference.gradual.base import BaseTypingValue
@@ -40,17 +39,14 @@ class TypeVarClass(BaseTypingValue):
return None return None
else: else:
safe_value = method(default=None) safe_value = method(default=None)
if self.inference_state.environment.version_info.major == 2: if isinstance(safe_value, str):
if isinstance(safe_value, bytes):
return force_unicode(safe_value)
if isinstance(safe_value, (str, unicode)):
return safe_value return safe_value
return None return None
class TypeVar(BaseTypingValue): class TypeVar(BaseTypingValue):
def __init__(self, parent_context, tree_name, var_name, unpacked_args): def __init__(self, parent_context, tree_name, var_name, unpacked_args):
super(TypeVar, self).__init__(parent_context, tree_name) super().__init__(parent_context, tree_name)
self._var_name = var_name self._var_name = var_name
self._constraints_lazy_values = [] self._constraints_lazy_values = []
@@ -124,7 +120,7 @@ class TypeVar(BaseTypingValue):
class TypeWrapper(ValueWrapper): class TypeWrapper(ValueWrapper):
def __init__(self, wrapped_value, original_value): def __init__(self, wrapped_value, original_value):
super(TypeWrapper, self).__init__(wrapped_value) super().__init__(wrapped_value)
self._original_value = original_value self._original_value = original_value
def execute_annotation(self): def execute_annotation(self):
+16 -17
View File
@@ -2,19 +2,20 @@ import os
import re import re
from functools import wraps from functools import wraps
from collections import namedtuple from collections import namedtuple
from pathlib import Path
from jedi import settings from jedi import settings
from jedi.file_io import FileIO from jedi.file_io import FileIO
from jedi._compatibility import FileNotFoundError, cast_path from jedi._compatibility import cast_path
from jedi.parser_utils import get_cached_code_lines from jedi.parser_utils import get_cached_code_lines
from jedi.inference.base_value import ValueSet, NO_VALUES from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference.gradual.stub_value import TypingModuleWrapper, StubModuleValue from jedi.inference.gradual.stub_value import TypingModuleWrapper, StubModuleValue
from jedi.inference.value import ModuleValue from jedi.inference.value import ModuleValue
_jedi_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) _jedi_path = Path(__file__).parent.parent.parent
TYPESHED_PATH = os.path.join(_jedi_path, 'third_party', 'typeshed') TYPESHED_PATH = _jedi_path.joinpath('third_party', 'typeshed')
DJANGO_INIT_PATH = os.path.join(_jedi_path, 'third_party', 'django-stubs', DJANGO_INIT_PATH = _jedi_path.joinpath('third_party', 'django-stubs',
'django-stubs', '__init__.pyi') 'django-stubs', '__init__.pyi')
_IMPORT_MAP = dict( _IMPORT_MAP = dict(
_collections='collections', _collections='collections',
@@ -38,8 +39,7 @@ def _create_stub_map(directory_path_info):
def generate(): def generate():
try: try:
listed = os.listdir(directory_path_info.path) listed = os.listdir(directory_path_info.path)
except (FileNotFoundError, OSError): except (FileNotFoundError, NotADirectoryError):
# OSError is Python 2
return return
for entry in listed: for entry in listed:
@@ -59,20 +59,19 @@ def _create_stub_map(directory_path_info):
def _get_typeshed_directories(version_info): def _get_typeshed_directories(version_info):
check_version_list = ['2and3', str(version_info.major)] check_version_list = ['2and3', '3']
for base in ['stdlib', 'third_party']: for base in ['stdlib', 'third_party']:
base_path = os.path.join(TYPESHED_PATH, base) base_path = TYPESHED_PATH.joinpath(base)
base_list = os.listdir(base_path) base_list = os.listdir(base_path)
for base_list_entry in base_list: for base_list_entry in base_list:
match = re.match(r'(\d+)\.(\d+)$', base_list_entry) match = re.match(r'(\d+)\.(\d+)$', base_list_entry)
if match is not None: if match is not None:
if int(match.group(1)) == version_info.major \ if match.group(1) == '3' and int(match.group(2)) <= version_info.minor:
and int(match.group(2)) <= version_info.minor:
check_version_list.append(base_list_entry) check_version_list.append(base_list_entry)
for check_version in check_version_list: for check_version in check_version_list:
is_third_party = base != 'stdlib' is_third_party = base != 'stdlib'
yield PathInfo(os.path.join(base_path, check_version), is_third_party) yield PathInfo(str(base_path.joinpath(check_version)), is_third_party)
_version_cache = {} _version_cache = {}
@@ -111,7 +110,7 @@ def import_module_decorator(func):
# ``os``. # ``os``.
python_value_set = ValueSet.from_sets( python_value_set = ValueSet.from_sets(
func(inference_state, (n,), None, sys_path,) func(inference_state, (n,), None, sys_path,)
for n in [u'posixpath', u'ntpath', u'macpath', u'os2emxpath'] for n in ['posixpath', 'ntpath', 'macpath', 'os2emxpath']
) )
else: else:
python_value_set = ValueSet.from_sets( python_value_set = ValueSet.from_sets(
@@ -183,7 +182,7 @@ def _try_to_load_stub(inference_state, import_names, python_value_set,
return _try_to_load_stub_from_file( return _try_to_load_stub_from_file(
inference_state, inference_state,
python_value_set, python_value_set,
file_io=FileIO(DJANGO_INIT_PATH), file_io=FileIO(str(DJANGO_INIT_PATH)),
import_names=import_names, import_names=import_names,
) )
@@ -198,8 +197,8 @@ def _try_to_load_stub(inference_state, import_names, python_value_set,
file_paths = [] file_paths = []
if c.is_namespace(): if c.is_namespace():
file_paths = [os.path.join(p, '__init__.pyi') for p in c.py__path__()] file_paths = [os.path.join(p, '__init__.pyi') for p in c.py__path__()]
elif file_path is not None and file_path.endswith('.py'): elif file_path is not None and file_path.suffix == '.py':
file_paths = [file_path + 'i'] file_paths = [str(file_path) + 'i']
for file_path in file_paths: for file_path in file_paths:
m = _try_to_load_stub_from_file( m = _try_to_load_stub_from_file(
@@ -274,7 +273,7 @@ def _load_from_typeshed(inference_state, python_value_set, parent_module_value,
def _try_to_load_stub_from_file(inference_state, python_value_set, file_io, import_names): def _try_to_load_stub_from_file(inference_state, python_value_set, file_io, import_names):
try: try:
stub_module_node = parse_stub_module(inference_state, file_io) stub_module_node = parse_stub_module(inference_state, file_io)
except (OSError, IOError): # IOError is Python 2 only except OSError:
# The file that you're looking for doesn't exist (anymore). # The file that you're looking for doesn't exist (anymore).
return None return None
else: else:
+7 -12
View File
@@ -7,7 +7,6 @@ This file deals with all the typing.py cases.
""" """
import itertools import itertools
from jedi._compatibility import unicode
from jedi import debug from jedi import debug
from jedi.inference.compiled import builtin_from_name, create_simple_object from jedi.inference.compiled import builtin_from_name, create_simple_object
from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \ from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \
@@ -72,7 +71,7 @@ class TypingModuleName(NameWrapper):
elif name == 'TYPE_CHECKING': elif name == 'TYPE_CHECKING':
# This is needed for e.g. imports that are only available for type # This is needed for e.g. imports that are only available for type
# checking or are in cycles. The user can then check this variable. # checking or are in cycles. The user can then check this variable.
yield builtin_from_name(inference_state, u'True') yield builtin_from_name(inference_state, 'True')
elif name == 'overload': elif name == 'overload':
yield OverloadFunction.create_cached( yield OverloadFunction.create_cached(
inference_state, self.parent_context, self.tree_name) inference_state, self.parent_context, self.tree_name)
@@ -89,12 +88,10 @@ class TypingModuleName(NameWrapper):
inference_state, self.parent_context, self.tree_name) inference_state, self.parent_context, self.tree_name)
elif name in ('no_type_check', 'no_type_check_decorator'): elif name in ('no_type_check', 'no_type_check_decorator'):
# This is not necessary, as long as we are not doing type checking. # This is not necessary, as long as we are not doing type checking.
for c in self._wrapped_name.infer(): # Fuck my life Python 2 yield from self._wrapped_name.infer()
yield c
else: else:
# Everything else shouldn't be relevant for type checking. # Everything else shouldn't be relevant for type checking.
for c in self._wrapped_name.infer(): # Fuck my life Python 2 yield from self._wrapped_name.infer()
yield c
class TypingModuleFilterWrapper(FilterWrapper): class TypingModuleFilterWrapper(FilterWrapper):
@@ -113,7 +110,7 @@ class ProxyWithGenerics(BaseTypingClassWithGenerics):
# Optional is basically just saying it's either None or the actual # Optional is basically just saying it's either None or the actual
# type. # type.
return self.gather_annotation_classes().execute_annotation() \ return self.gather_annotation_classes().execute_annotation() \
| ValueSet([builtin_from_name(self.inference_state, u'None')]) | ValueSet([builtin_from_name(self.inference_state, 'None')])
elif string_name == 'Type': elif string_name == 'Type':
# The type is actually already given in the index_value # The type is actually already given in the index_value
return self._generics_manager[0] return self._generics_manager[0]
@@ -156,7 +153,7 @@ class ProxyWithGenerics(BaseTypingClassWithGenerics):
# Optional[T] is equivalent to Union[T, None]. In Jedi unions # Optional[T] is equivalent to Union[T, None]. In Jedi unions
# are represented by members within a ValueSet, so we extract # are represented by members within a ValueSet, so we extract
# the T from the Optional[T] by removing the None value. # the T from the Optional[T] by removing the None value.
none = builtin_from_name(self.inference_state, u'None') none = builtin_from_name(self.inference_state, 'None')
return annotation_generics[0].infer_type_vars( return annotation_generics[0].infer_type_vars(
value_set.filter(lambda x: x != none), value_set.filter(lambda x: x != none),
) )
@@ -263,8 +260,6 @@ class TypeAlias(LazyValueWrapper):
def _get_wrapped_value(self): def _get_wrapped_value(self):
module_name, class_name = self._actual.split('.') module_name, class_name = self._actual.split('.')
if self.inference_state.environment.version_info.major == 2 and module_name == 'builtins':
module_name = '__builtin__'
# TODO use inference_state.import_module? # TODO use inference_state.import_module?
from jedi.inference.imports import Importer from jedi.inference.imports import Importer
@@ -418,7 +413,7 @@ class NewTypeFunction(BaseTypingValue):
class NewType(Value): class NewType(Value):
def __init__(self, inference_state, parent_context, tree_node, type_value_set): def __init__(self, inference_state, parent_context, tree_node, type_value_set):
super(NewType, self).__init__(inference_state, parent_context) super().__init__(inference_state, parent_context)
self._type_value_set = type_value_set self._type_value_set = type_value_set
self.tree_node = tree_node self.tree_node = tree_node
@@ -461,7 +456,7 @@ class TypedDict(LazyValueWrapper):
return ValueName(self, self.tree_node.name) return ValueName(self, self.tree_node.name)
def py__simple_getitem__(self, index): def py__simple_getitem__(self, index):
if isinstance(index, unicode): if isinstance(index, str):
return ValueSet.from_sets( return ValueSet.from_sets(
name.infer() name.infer()
for filter in self._definition_class.get_filters(is_instance=True) for filter in self._definition_class.get_filters(is_instance=True)
+13 -8
View File
@@ -1,4 +1,5 @@
import os import os
from pathlib import Path
from jedi.inference.gradual.typeshed import TYPESHED_PATH, create_stub_module from jedi.inference.gradual.typeshed import TYPESHED_PATH, create_stub_module
@@ -9,14 +10,18 @@ def load_proper_stub_module(inference_state, file_io, import_names, module_node)
module. module.
""" """
path = file_io.path path = file_io.path
assert path.endswith('.pyi') path = Path(path)
if path.startswith(TYPESHED_PATH): assert path.suffix == '.pyi'
# /foo/stdlib/3/os/__init__.pyi -> stdlib/3/os/__init__ try:
rest = path[len(TYPESHED_PATH) + 1: -4] relative_path = path.relative_to(TYPESHED_PATH)
split_paths = tuple(rest.split(os.path.sep)) except ValueError:
# Remove the stdlib/3 or third_party/3.5 part pass
import_names = split_paths[2:] else:
if import_names[-1] == '__init__': # /[...]/stdlib/3/os/__init__.pyi -> stdlib/3/os/__init__
rest = relative_path.with_suffix('')
# Remove the stdlib/3 or third_party/3.6 part
import_names = rest.parts[2:]
if rest.name == '__init__':
import_names = import_names[:-1] import_names = import_names[:-1]
if import_names is not None: if import_names is not None:
+6 -11
View File
@@ -7,18 +7,17 @@ from contextlib import contextmanager
from parso.python import tree from parso.python import tree
from jedi._compatibility import unicode
def is_stdlib_path(path): def is_stdlib_path(path):
# Python standard library paths look like this: # Python standard library paths look like this:
# /usr/lib/python3.5/... # /usr/lib/python3.9/...
# TODO The implementation below is probably incorrect and not complete. # TODO The implementation below is probably incorrect and not complete.
if 'dist-packages' in path or 'site-packages' in path: parts = path.parts
if 'dist-packages' in parts or 'site-packages' in parts:
return False return False
base_path = os.path.join(sys.prefix, 'lib', 'python') base_path = os.path.join(sys.prefix, 'lib', 'python')
return bool(re.match(re.escape(base_path) + r'\d.\d', path)) return bool(re.match(re.escape(base_path) + r'\d.\d', str(path)))
def deep_ast_copy(obj): def deep_ast_copy(obj):
@@ -122,11 +121,7 @@ def get_names_of_node(node):
def is_string(value): def is_string(value):
if value.inference_state.environment.version_info.major == 2: return value.is_compiled() and isinstance(value.get_safe_value(default=None), str)
str_classes = (unicode, bytes)
else:
str_classes = (unicode,)
return value.is_compiled() and isinstance(value.get_safe_value(default=None), str_classes)
def is_literal(value): def is_literal(value):
@@ -144,7 +139,7 @@ def get_int_or_none(value):
def get_str_or_none(value): def get_str_or_none(value):
return _get_safe_value_or_none(value, (bytes, unicode)) return _get_safe_value_or_none(value, str)
def is_number(value): def is_number(value):
+12 -11
View File
@@ -5,18 +5,15 @@ not any actual importing done. This module is about finding modules in the
filesystem. This can be quite tricky sometimes, because Python imports are not filesystem. This can be quite tricky sometimes, because Python imports are not
always that simple. always that simple.
This module uses imp for python up to 3.2 and importlib for python 3.3 on; the
correct implementation is delegated to _compatibility.
This module also supports import autocompletion, which means to complete This module also supports import autocompletion, which means to complete
statements like ``from datetim`` (cursor at the end would return ``datetime``). statements like ``from datetim`` (cursor at the end would return ``datetime``).
""" """
import os import os
from pathlib import Path
from parso.python import tree from parso.python import tree
from parso.tree import search_ancestor from parso.tree import search_ancestor
from jedi._compatibility import ImplicitNSInfo, force_unicode, FileNotFoundError
from jedi import debug from jedi import debug
from jedi import settings from jedi import settings
from jedi.file_io import FolderIO from jedi.file_io import FolderIO
@@ -31,6 +28,7 @@ from jedi.inference.names import ImportName, SubModuleName
from jedi.inference.base_value import ValueSet, NO_VALUES from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference.gradual.typeshed import import_module_decorator, \ from jedi.inference.gradual.typeshed import import_module_decorator, \
create_stub_module, parse_stub_module create_stub_module, parse_stub_module
from jedi.inference.compiled.subprocess.functions import ImplicitNSInfo
from jedi.plugins import plugin_manager from jedi.plugins import plugin_manager
@@ -211,7 +209,7 @@ class Importer(object):
# somewhere out of the filesystem. # somewhere out of the filesystem.
self._infer_possible = False self._infer_possible = False
else: else:
self._fixed_sys_path = [force_unicode(base_directory)] self._fixed_sys_path = [base_directory]
if base_import_path is None: if base_import_path is None:
if import_path: if import_path:
@@ -240,7 +238,10 @@ class Importer(object):
# inference we want to show the user as much as possible. # inference we want to show the user as much as possible.
# See GH #1446. # See GH #1446.
self._inference_state.get_sys_path(add_init_paths=not is_completion) self._inference_state.get_sys_path(add_init_paths=not is_completion)
+ sys_path.check_sys_path_modifications(self._module_context) + [
str(p) for p
in sys_path.check_sys_path_modifications(self._module_context)
]
) )
def follow(self): def follow(self):
@@ -332,7 +333,7 @@ def import_module_by_names(inference_state, import_names, sys_path=None,
sys_path = inference_state.get_sys_path() sys_path = inference_state.get_sys_path()
str_import_names = tuple( str_import_names = tuple(
force_unicode(i.value if isinstance(i, tree.Name) else i) i.value if isinstance(i, tree.Name) else i
for i in import_names for i in import_names
) )
value_set = [None] value_set = [None]
@@ -470,19 +471,19 @@ def load_module_from_path(inference_state, file_io, import_names=None, is_packag
here to ensure that a random path is still properly loaded into the Jedi here to ensure that a random path is still properly loaded into the Jedi
module structure. module structure.
""" """
path = file_io.path path = Path(file_io.path)
if import_names is None: if import_names is None:
e_sys_path = inference_state.get_sys_path() e_sys_path = inference_state.get_sys_path()
import_names, is_package = sys_path.transform_path_to_dotted(e_sys_path, path) import_names, is_package = sys_path.transform_path_to_dotted(e_sys_path, path)
else: else:
assert isinstance(is_package, bool) assert isinstance(is_package, bool)
is_stub = file_io.path.endswith('.pyi') is_stub = path.suffix == '.pyi'
if is_stub: if is_stub:
folder_io = file_io.get_parent_folder() folder_io = file_io.get_parent_folder()
if folder_io.path.endswith('-stubs'): if folder_io.path.endswith('-stubs'):
folder_io = FolderIO(folder_io.path[:-6]) folder_io = FolderIO(folder_io.path[:-6])
if file_io.path.endswith('__init__.pyi'): if path.name == '__init__.pyi':
python_file_io = folder_io.get_file_io('__init__.py') python_file_io = folder_io.get_file_io('__init__.py')
else: else:
python_file_io = folder_io.get_file_io(import_names[-1] + '.py') python_file_io = folder_io.get_file_io(import_names[-1] + '.py')
@@ -513,7 +514,7 @@ def load_module_from_path(inference_state, file_io, import_names=None, is_packag
def load_namespace_from_path(inference_state, folder_io): def load_namespace_from_path(inference_state, folder_io):
import_names, is_package = sys_path.transform_path_to_dotted( import_names, is_package = sys_path.transform_path_to_dotted(
inference_state.get_sys_path(), inference_state.get_sys_path(),
folder_io.path Path(folder_io.path)
) )
from jedi.inference.value.namespace import ImplicitNamespaceValue from jedi.inference.value.namespace import ImplicitNamespaceValue
return ImplicitNamespaceValue(inference_state, import_names, [folder_io.path]) return ImplicitNamespaceValue(inference_state, import_names, [folder_io.path])
+2 -2
View File
@@ -29,7 +29,7 @@ class LazyKnownValues(AbstractLazyValue):
class LazyUnknownValue(AbstractLazyValue): class LazyUnknownValue(AbstractLazyValue):
def __init__(self, min=1, max=1): def __init__(self, min=1, max=1):
super(LazyUnknownValue, self).__init__(None, min, max) super().__init__(None, min, max)
def infer(self): def infer(self):
return NO_VALUES return NO_VALUES
@@ -37,7 +37,7 @@ class LazyUnknownValue(AbstractLazyValue):
class LazyTreeValue(AbstractLazyValue): class LazyTreeValue(AbstractLazyValue):
def __init__(self, context, node, min=1, max=1): def __init__(self, context, node, min=1, max=1):
super(LazyTreeValue, self).__init__(node, min, max) super().__init__(node, min, max)
self.context = context self.context = context
# We need to save the predefined names. It's an unfortunate side effect # We need to save the predefined names. It's an unfortunate side effect
# that needs to be tracked otherwise results will be wrong. # that needs to be tracked otherwise results will be wrong.
+12 -12
View File
@@ -1,8 +1,8 @@
from abc import abstractmethod from abc import abstractmethod
from inspect import Parameter
from parso.tree import search_ancestor from parso.tree import search_ancestor
from jedi._compatibility import Parameter
from jedi.parser_utils import find_statement_documentation, clean_scope_docstring from jedi.parser_utils import find_statement_documentation, clean_scope_docstring
from jedi.inference.utils import unite from jedi.inference.utils import unite
from jedi.inference.base_value import ValueSet, NO_VALUES from jedi.inference.base_value import ValueSet, NO_VALUES
@@ -123,7 +123,7 @@ class AbstractTreeName(AbstractNameDefinition):
else: else:
return None return None
return super(AbstractTreeName, self).get_qualified_names(include_module_names) return super().get_qualified_names(include_module_names)
def _get_qualified_names(self): def _get_qualified_names(self):
parent_names = self.parent_context.get_qualified_names() parent_names = self.parent_context.get_qualified_names()
@@ -242,7 +242,7 @@ class ValueNameMixin(object):
def get_root_context(self): def get_root_context(self):
if self.parent_context is None: # A module if self.parent_context is None: # A module
return self._value.as_context() return self._value.as_context()
return super(ValueNameMixin, self).get_root_context() return super().get_root_context()
def get_defining_qualified_value(self): def get_defining_qualified_value(self):
context = self.parent_context context = self.parent_context
@@ -257,7 +257,7 @@ class ValueNameMixin(object):
class ValueName(ValueNameMixin, AbstractTreeName): class ValueName(ValueNameMixin, AbstractTreeName):
def __init__(self, value, tree_name): def __init__(self, value, tree_name):
super(ValueName, self).__init__(value.parent_context, tree_name) super().__init__(value.parent_context, tree_name)
self._value = value self._value = value
def goto(self): def goto(self):
@@ -372,7 +372,7 @@ class _ParamMixin(object):
class ParamNameInterface(_ParamMixin): class ParamNameInterface(_ParamMixin):
api_type = u'param' api_type = 'param'
def get_kind(self): def get_kind(self):
raise NotImplementedError raise NotImplementedError
@@ -429,7 +429,7 @@ class BaseTreeParamName(ParamNameInterface, AbstractTreeName):
class _ActualTreeParamName(BaseTreeParamName): class _ActualTreeParamName(BaseTreeParamName):
def __init__(self, function_value, tree_name): def __init__(self, function_value, tree_name):
super(_ActualTreeParamName, self).__init__( super().__init__(
function_value.get_default_param_context(), tree_name) function_value.get_default_param_context(), tree_name)
self.function_value = function_value self.function_value = function_value
@@ -499,11 +499,11 @@ class _ActualTreeParamName(BaseTreeParamName):
class AnonymousParamName(_ActualTreeParamName): class AnonymousParamName(_ActualTreeParamName):
@plugin_manager.decorate(name='goto_anonymous_param') @plugin_manager.decorate(name='goto_anonymous_param')
def goto(self): def goto(self):
return super(AnonymousParamName, self).goto() return super().goto()
@plugin_manager.decorate(name='infer_anonymous_param') @plugin_manager.decorate(name='infer_anonymous_param')
def infer(self): def infer(self):
values = super(AnonymousParamName, self).infer() values = super().infer()
if values: if values:
return values return values
from jedi.inference.dynamic_params import dynamic_param_lookup from jedi.inference.dynamic_params import dynamic_param_lookup
@@ -527,11 +527,11 @@ class AnonymousParamName(_ActualTreeParamName):
class ParamName(_ActualTreeParamName): class ParamName(_ActualTreeParamName):
def __init__(self, function_value, tree_name, arguments): def __init__(self, function_value, tree_name, arguments):
super(ParamName, self).__init__(function_value, tree_name) super().__init__(function_value, tree_name)
self.arguments = arguments self.arguments = arguments
def infer(self): def infer(self):
values = super(ParamName, self).infer() values = super().infer()
if values: if values:
return values return values
@@ -627,7 +627,7 @@ class StubNameMixin(object):
names = convert_names(names, prefer_stub_to_compiled=False) names = convert_names(names, prefer_stub_to_compiled=False)
if self in names: if self in names:
return super(StubNameMixin, self).py__doc__() return super().py__doc__()
else: else:
# We have signatures ourselves in stubs, so don't use signatures # We have signatures ourselves in stubs, so don't use signatures
# from the implementation. # from the implementation.
@@ -637,7 +637,7 @@ class StubNameMixin(object):
# From here on down we make looking up the sys.version_info fast. # From here on down we make looking up the sys.version_info fast.
class StubName(StubNameMixin, TreeNameDefinition): class StubName(StubNameMixin, TreeNameDefinition):
def infer(self): def infer(self):
inferred = super(StubName, self).infer() inferred = super().infer()
if self.string_name == 'version_info' and self.get_root_context().py__name__() == 'sys': if self.string_name == 'version_info' and self.get_root_context().py__name__() == 'sys':
from jedi.inference.gradual.stub_value import VersionInfo from jedi.inference.gradual.stub_value import VersionInfo
return ValueSet(VersionInfo(c) for c in inferred) return ValueSet(VersionInfo(c) for c in inferred)
+2 -3
View File
@@ -1,4 +1,5 @@
from collections import defaultdict from collections import defaultdict
from inspect import Parameter
from jedi import debug from jedi import debug
from jedi.inference.utils import PushBackIterator from jedi.inference.utils import PushBackIterator
@@ -6,7 +7,6 @@ from jedi.inference import analysis
from jedi.inference.lazy_value import LazyKnownValue, \ from jedi.inference.lazy_value import LazyKnownValue, \
LazyTreeValue, LazyUnknownValue LazyTreeValue, LazyUnknownValue
from jedi.inference.value import iterable from jedi.inference.value import iterable
from jedi._compatibility import Parameter
from jedi.inference.names import ParamName from jedi.inference.names import ParamName
@@ -20,8 +20,7 @@ def _add_argument_issue(error_name, lazy_value, message):
class ExecutedParamName(ParamName): class ExecutedParamName(ParamName):
def __init__(self, function_value, arguments, param_node, lazy_value, is_default=False): def __init__(self, function_value, arguments, param_node, lazy_value, is_default=False):
super(ExecutedParamName, self).__init__( super().__init__(function_value, param_node.name, arguments=arguments)
function_value, param_node.name, arguments=arguments)
self._lazy_value = lazy_value self._lazy_value = lazy_value
self._is_default = is_default self._is_default = is_default
+2 -4
View File
@@ -3,7 +3,6 @@ import re
from parso import python_bytes_to_unicode from parso import python_bytes_to_unicode
from jedi._compatibility import FileNotFoundError
from jedi.debug import dbg from jedi.debug import dbg
from jedi.file_io import KnownContentFileIO from jedi.file_io import KnownContentFileIO
from jedi.inference.imports import SubModuleName, load_module_from_path from jedi.inference.imports import SubModuleName, load_module_from_path
@@ -273,9 +272,8 @@ def get_module_contexts_containing_name(inference_state, module_contexts, name,
return return
file_io_iterator = _find_python_files_in_sys_path(inference_state, module_contexts) file_io_iterator = _find_python_files_in_sys_path(inference_state, module_contexts)
for x in search_in_file_ios(inference_state, file_io_iterator, name, yield from search_in_file_ios(inference_state, file_io_iterator, name,
limit_reduction=limit_reduction): limit_reduction=limit_reduction)
yield x # Python 2...
def search_in_file_ios(inference_state, file_io_iterator, name, limit_reduction=1): def search_in_file_ios(inference_state, file_io_iterator, name, limit_reduction=1):
+4 -3
View File
@@ -1,4 +1,5 @@
from jedi._compatibility import Parameter from inspect import Parameter
from jedi.cache import memoize_method from jedi.cache import memoize_method
from jedi import debug from jedi import debug
from jedi import parser_utils from jedi import parser_utils
@@ -67,7 +68,7 @@ class AbstractSignature(_SignatureMixin):
class TreeSignature(AbstractSignature): class TreeSignature(AbstractSignature):
def __init__(self, value, function_value=None, is_bound=False): def __init__(self, value, function_value=None, is_bound=False):
super(TreeSignature, self).__init__(value, is_bound) super().__init__(value, is_bound)
self._function_value = function_value or value self._function_value = function_value or value
def bind(self, value): def bind(self, value):
@@ -121,7 +122,7 @@ class TreeSignature(AbstractSignature):
class BuiltinSignature(AbstractSignature): class BuiltinSignature(AbstractSignature):
def __init__(self, value, return_string, function_value=None, is_bound=False): def __init__(self, value, return_string, function_value=None, is_bound=False):
super(BuiltinSignature, self).__init__(value, is_bound) super().__init__(value, is_bound)
self._return_string = return_string self._return_string = return_string
self.__function_value = function_value self.__function_value = function_value
+2 -4
View File
@@ -10,8 +10,8 @@ This means for example in this case::
The signature here for bar should be `bar(b, c)` instead of bar(*args). The signature here for bar should be `bar(b, c)` instead of bar(*args).
""" """
from inspect import Parameter
from jedi._compatibility import Parameter
from jedi.inference.utils import to_list from jedi.inference.utils import to_list
from jedi.inference.names import ParamNameWrapper from jedi.inference.names import ParamNameWrapper
from jedi.inference.helpers import is_big_annoying_library from jedi.inference.helpers import is_big_annoying_library
@@ -32,8 +32,6 @@ def _iter_nodes_for_param(param_name):
argument = name.parent argument = name.parent
if argument.type == 'argument' \ if argument.type == 'argument' \
and argument.children[0] == '*' * param_name.star_count: and argument.children[0] == '*' * param_name.star_count:
# No support for Python 2.7 here, but they are end-of-life
# anyway
trailer = search_ancestor(argument, 'trailer') trailer = search_ancestor(argument, 'trailer')
if trailer is not None: # Make sure we're in a function if trailer is not None: # Make sure we're in a function
context = execution_context.create_context(trailer) context = execution_context.create_context(trailer)
@@ -210,7 +208,7 @@ def process_params(param_names, star_count=3): # default means both * and **
class ParamNameFixedKind(ParamNameWrapper): class ParamNameFixedKind(ParamNameWrapper):
def __init__(self, param_name, new_kind): def __init__(self, param_name, new_kind):
super(ParamNameFixedKind, self).__init__(param_name) super().__init__(param_name)
self._new_kind = new_kind self._new_kind = new_kind
def get_kind(self): def get_kind(self):
+8 -19
View File
@@ -5,7 +5,6 @@ import copy
from parso.python import tree from parso.python import tree
from jedi._compatibility import force_unicode, unicode
from jedi import debug from jedi import debug
from jedi import parser_utils from jedi import parser_utils
from jedi.inference.base_value import ValueSet, NO_VALUES, ContextualizedNode, \ from jedi.inference.base_value import ValueSet, NO_VALUES, ContextualizedNode, \
@@ -225,12 +224,10 @@ def _infer_node(context, element):
| context.infer_node(element.children[-1])) | context.infer_node(element.children[-1]))
elif typ == 'operator': elif typ == 'operator':
# Must be an ellipsis, other operators are not inferred. # Must be an ellipsis, other operators are not inferred.
# In Python 2 ellipsis is coded as three single dot tokens, not if element.value != '...':
# as one token 3 dot token.
if element.value not in ('.', '...'):
origin = element.parent origin = element.parent
raise AssertionError("unhandled operator %s in %s " % (repr(element.value), origin)) raise AssertionError("unhandled operator %s in %s " % (repr(element.value), origin))
return ValueSet([compiled.builtin_from_name(inference_state, u'Ellipsis')]) return ValueSet([compiled.builtin_from_name(inference_state, 'Ellipsis')])
elif typ == 'dotted_name': elif typ == 'dotted_name':
value_set = infer_atom(context, element.children[0]) value_set = infer_atom(context, element.children[0])
for next_name in element.children[2::2]: for next_name in element.children[2::2]:
@@ -289,10 +286,6 @@ def infer_atom(context, atom):
""" """
state = context.inference_state state = context.inference_state
if atom.type == 'name': if atom.type == 'name':
if atom.value in ('True', 'False', 'None'):
# Python 2...
return ValueSet([compiled.builtin_from_name(state, atom.value)])
# This is the first global lookup. # This is the first global lookup.
stmt = tree.search_ancestor( stmt = tree.search_ancestor(
atom, 'expr_stmt', 'lambdef' atom, 'expr_stmt', 'lambdef'
@@ -312,9 +305,6 @@ def infer_atom(context, atom):
# For False/True/None # For False/True/None
if atom.value in ('False', 'True', 'None'): if atom.value in ('False', 'True', 'None'):
return ValueSet([compiled.builtin_from_name(state, atom.value)]) return ValueSet([compiled.builtin_from_name(state, atom.value)])
elif atom.value == 'print':
# print e.g. could be inferred like this in Python 2.7
return NO_VALUES
elif atom.value == 'yield': elif atom.value == 'yield':
# Contrary to yield from, yield can just appear alone to return a # Contrary to yield from, yield can just appear alone to return a
# value when used with `.send()`. # value when used with `.send()`.
@@ -329,7 +319,7 @@ def infer_atom(context, atom):
value_set = infer_atom(context, atom.children[0]) value_set = infer_atom(context, atom.children[0])
for string in atom.children[1:]: for string in atom.children[1:]:
right = infer_atom(context, string) right = infer_atom(context, string)
value_set = _infer_comparison(context, value_set, u'+', right) value_set = _infer_comparison(context, value_set, '+', right)
return value_set return value_set
elif atom.type == 'fstring': elif atom.type == 'fstring':
return compiled.get_string_value_set(state) return compiled.get_string_value_set(state)
@@ -567,7 +557,7 @@ def _is_tuple(value):
def _bool_to_value(inference_state, bool_): def _bool_to_value(inference_state, bool_):
return compiled.builtin_from_name(inference_state, force_unicode(str(bool_))) return compiled.builtin_from_name(inference_state, str(bool_))
def _get_tuple_ints(value): def _get_tuple_ints(value):
@@ -590,10 +580,10 @@ def _get_tuple_ints(value):
def _infer_comparison_part(inference_state, context, left, operator, right): def _infer_comparison_part(inference_state, context, left, operator, right):
l_is_num = is_number(left) l_is_num = is_number(left)
r_is_num = is_number(right) r_is_num = is_number(right)
if isinstance(operator, unicode): if isinstance(operator, str):
str_operator = operator str_operator = operator
else: else:
str_operator = force_unicode(str(operator.value)) str_operator = str(operator.value)
if str_operator == '*': if str_operator == '*':
# for iterables, ignore * operations # for iterables, ignore * operations
@@ -747,7 +737,7 @@ def tree_name_to_values(inference_state, context, tree_name):
types = infer_expr_stmt(context, node, tree_name) types = infer_expr_stmt(context, node, tree_name)
elif typ == 'with_stmt': elif typ == 'with_stmt':
value_managers = context.infer_node(node.get_test_node_from_name(tree_name)) value_managers = context.infer_node(node.get_test_node_from_name(tree_name))
enter_methods = value_managers.py__getattribute__(u'__enter__') enter_methods = value_managers.py__getattribute__('__enter__')
return enter_methods.execute_with_values() return enter_methods.execute_with_values()
elif typ in ('import_from', 'import_name'): elif typ in ('import_from', 'import_name'):
types = imports.infer_import(context, tree_name) types = imports.infer_import(context, tree_name)
@@ -861,8 +851,7 @@ def _infer_subscript_list(context, index):
return ValueSet([iterable.Slice(context, None, None, None)]) return ValueSet([iterable.Slice(context, None, None, None)])
elif index.type == 'subscript' and not index.children[0] == '.': elif index.type == 'subscript' and not index.children[0] == '.':
# subscript basically implies a slice operation, except for Python 2's # subscript basically implies a slice operation
# Ellipsis.
# e.g. array[:3] # e.g. array[:3]
result = [] result = []
for el in index.children: for el in index.children:
+26 -26
View File
@@ -1,11 +1,11 @@
import os import os
import re import re
from pathlib import Path
from importlib.machinery import all_suffixes
from jedi._compatibility import unicode, force_unicode, all_suffixes
from jedi.inference.cache import inference_state_method_cache from jedi.inference.cache import inference_state_method_cache
from jedi.inference.base_value import ContextualizedNode from jedi.inference.base_value import ContextualizedNode
from jedi.inference.helpers import is_string, get_str_or_none from jedi.inference.helpers import is_string, get_str_or_none
from jedi.common import traverse_parents
from jedi.parser_utils import get_cached_code_lines from jedi.parser_utils import get_cached_code_lines
from jedi.file_io import FileIO from jedi.file_io import FileIO
from jedi import settings from jedi import settings
@@ -14,8 +14,9 @@ from jedi import debug
_BUILDOUT_PATH_INSERTION_LIMIT = 10 _BUILDOUT_PATH_INSERTION_LIMIT = 10
def _abs_path(module_context, path): def _abs_path(module_context, path: str):
if os.path.isabs(path): path = Path(path)
if path.is_absolute():
return path return path
module_path = module_context.py__file__() module_path = module_context.py__file__()
@@ -24,9 +25,8 @@ def _abs_path(module_context, path):
# system. # system.
return None return None
base_dir = os.path.dirname(module_path) base_dir = module_path.parent
path = force_unicode(path) return base_dir.joinpath(path).absolute()
return os.path.abspath(os.path.join(base_dir, path))
def _paths_from_assignment(module_context, expr_stmt): def _paths_from_assignment(module_context, expr_stmt):
@@ -148,7 +148,7 @@ def discover_buildout_paths(inference_state, script_path):
def _get_paths_from_buildout_script(inference_state, buildout_script_path): def _get_paths_from_buildout_script(inference_state, buildout_script_path):
file_io = FileIO(buildout_script_path) file_io = FileIO(str(buildout_script_path))
try: try:
module_node = inference_state.parse( module_node = inference_state.parse(
file_io=file_io, file_io=file_io,
@@ -164,20 +164,20 @@ def _get_paths_from_buildout_script(inference_state, buildout_script_path):
inference_state, module_node, inference_state, module_node,
file_io=file_io, file_io=file_io,
string_names=None, string_names=None,
code_lines=get_cached_code_lines(inference_state.grammar, buildout_script_path), code_lines=get_cached_code_lines(inference_state.grammar, str(buildout_script_path)),
).as_context() ).as_context()
for path in check_sys_path_modifications(module_context): for path in check_sys_path_modifications(module_context):
yield path yield path
def _get_parent_dir_with_file(path, filename): def _get_parent_dir_with_file(path: Path, filename):
for parent in traverse_parents(path): for parent in path.parents:
if os.path.isfile(os.path.join(parent, filename)): if parent.joinpath(filename).is_file():
return parent return parent
return None return None
def _get_buildout_script_paths(search_path): def _get_buildout_script_paths(search_path: Path):
""" """
if there is a 'buildout.cfg' file in one of the parent directories of the if there is a 'buildout.cfg' file in one of the parent directories of the
given module it will return a list of all files in the buildout bin given module it will return a list of all files in the buildout bin
@@ -189,13 +189,13 @@ def _get_buildout_script_paths(search_path):
project_root = _get_parent_dir_with_file(search_path, 'buildout.cfg') project_root = _get_parent_dir_with_file(search_path, 'buildout.cfg')
if not project_root: if not project_root:
return return
bin_path = os.path.join(project_root, 'bin') bin_path = project_root.joinpath('bin')
if not os.path.exists(bin_path): if not bin_path.exists():
return return
for filename in os.listdir(bin_path): for filename in os.listdir(bin_path):
try: try:
filepath = os.path.join(bin_path, filename) filepath = bin_path.joinpath(filename)
with open(filepath, 'r') as f: with open(filepath, 'r') as f:
firstline = f.readline() firstline = f.readline()
if firstline.startswith('#!') and 'python' in firstline: if firstline.startswith('#!') and 'python' in firstline:
@@ -203,14 +203,14 @@ def _get_buildout_script_paths(search_path):
except (UnicodeDecodeError, IOError) as e: except (UnicodeDecodeError, IOError) as e:
# Probably a binary file; permission error or race cond. because # Probably a binary file; permission error or race cond. because
# file got deleted. Ignore it. # file got deleted. Ignore it.
debug.warning(unicode(e)) debug.warning(e)
continue continue
def remove_python_path_suffix(path): def remove_python_path_suffix(path):
for suffix in all_suffixes() + ['.pyi']: for suffix in all_suffixes() + ['.pyi']:
if path.endswith(suffix): if path.suffix == suffix:
path = path[:-len(suffix)] path = path.with_name(path.stem)
break break
return path return path
@@ -219,8 +219,7 @@ def transform_path_to_dotted(sys_path, module_path):
""" """
Returns the dotted path inside a sys.path as a list of names. e.g. Returns the dotted path inside a sys.path as a list of names. e.g.
>>> from os.path import abspath >>> transform_path_to_dotted([str(Path("/foo").absolute())], Path('/foo/bar/baz.py').absolute())
>>> transform_path_to_dotted([abspath("/foo")], abspath('/foo/bar/baz.py'))
(('bar', 'baz'), False) (('bar', 'baz'), False)
Returns (None, False) if the path doesn't really resolve to anything. Returns (None, False) if the path doesn't really resolve to anything.
@@ -228,21 +227,22 @@ def transform_path_to_dotted(sys_path, module_path):
""" """
# First remove the suffix. # First remove the suffix.
module_path = remove_python_path_suffix(module_path) module_path = remove_python_path_suffix(module_path)
if module_path.name.startswith('.'):
return None, False
# Once the suffix was removed we are using the files as we know them. This # Once the suffix was removed we are using the files as we know them. This
# means that if someone uses an ending like .vim for a Python file, .vim # means that if someone uses an ending like .vim for a Python file, .vim
# will be part of the returned dotted part. # will be part of the returned dotted part.
is_package = module_path.endswith(os.path.sep + '__init__') is_package = module_path.name == '__init__'
if is_package: if is_package:
# -1 to remove the separator module_path = module_path.parent
module_path = module_path[:-len('__init__') - 1]
def iter_potential_solutions(): def iter_potential_solutions():
for p in sys_path: for p in sys_path:
if module_path.startswith(p): if str(module_path).startswith(p):
# Strip the trailing slash/backslash # Strip the trailing slash/backslash
rest = module_path[len(p):] rest = str(module_path)[len(p):]
# On Windows a path can also use a slash. # On Windows a path can also use a slash.
if rest.startswith(os.path.sep) or rest.startswith('/'): if rest.startswith(os.path.sep) or rest.startswith('/'):
# Remove a slash in cases it's still there. # Remove a slash in cases it's still there.
+2 -27
View File
@@ -1,12 +1,8 @@
""" A universal module with functions / classes without dependencies. """ """ A universal module with functions / classes without dependencies. """
import sys
import contextlib
import functools import functools
import re import re
import os import os
from jedi._compatibility import reraise
_sep = os.path.sep _sep = os.path.sep
if os.path.altsep is not None: if os.path.altsep is not None:
@@ -36,7 +32,6 @@ class UncaughtAttributeError(Exception):
""" """
Important, because `__getattr__` and `hasattr` catch AttributeErrors Important, because `__getattr__` and `hasattr` catch AttributeErrors
implicitly. This is really evil (mainly because of `__getattr__`). implicitly. This is really evil (mainly because of `__getattr__`).
`hasattr` in Python 2 is even more evil, because it catches ALL exceptions.
Therefore this class originally had to be derived from `BaseException` Therefore this class originally had to be derived from `BaseException`
instead of `Exception`. But because I removed relevant `hasattr` from instead of `Exception`. But because I removed relevant `hasattr` from
the code base, we can now switch back to `Exception`. the code base, we can now switch back to `Exception`.
@@ -65,17 +60,13 @@ def reraise_uncaught(func):
difficult. This decorator is to help us getting there by changing difficult. This decorator is to help us getting there by changing
`AttributeError` to `UncaughtAttributeError` to avoid unexpected catch. `AttributeError` to `UncaughtAttributeError` to avoid unexpected catch.
This helps us noticing bugs earlier and facilitates debugging. This helps us noticing bugs earlier and facilitates debugging.
.. note:: Treating StopIteration here is easy.
Add that feature when needed.
""" """
@functools.wraps(func) @functools.wraps(func)
def wrapper(*args, **kwds): def wrapper(*args, **kwds):
try: try:
return func(*args, **kwds) return func(*args, **kwds)
except AttributeError: except AttributeError as e:
exc_info = sys.exc_info() raise UncaughtAttributeError(e) from e
reraise(UncaughtAttributeError(exc_info[1]), exc_info[2])
return wrapper return wrapper
@@ -91,25 +82,9 @@ class PushBackIterator(object):
def __iter__(self): def __iter__(self):
return self return self
def next(self):
""" Python 2 Compatibility """
return self.__next__()
def __next__(self): def __next__(self):
if self.pushes: if self.pushes:
self.current = self.pushes.pop() self.current = self.pushes.pop()
else: else:
self.current = next(self.iterator) self.current = next(self.iterator)
return self.current return self.current
@contextlib.contextmanager
def ignored(*exceptions):
"""
Value manager that ignores all of the specified exceptions. This will
be in the standard library starting with Python 3.5.
"""
try:
yield
except exceptions:
pass
+1 -1
View File
@@ -8,7 +8,7 @@ from jedi.inference.base_value import ValueWrapper, ValueSet
class Decoratee(ValueWrapper): class Decoratee(ValueWrapper):
def __init__(self, wrapped_value, original_value): def __init__(self, wrapped_value, original_value):
super(Decoratee, self).__init__(wrapped_value) super().__init__(wrapped_value)
self._original_value = original_value self._original_value = original_value
def py__doc__(self): def py__doc__(self):
+1 -1
View File
@@ -170,7 +170,7 @@ class _DynamicArrayAdditions(HelperValueMixin):
class _Modification(ValueWrapper): class _Modification(ValueWrapper):
def __init__(self, wrapped_value, assigned_values, contextualized_key): def __init__(self, wrapped_value, assigned_values, contextualized_key):
super(_Modification, self).__init__(wrapped_value) super().__init__(wrapped_value)
self._assigned_values = assigned_values self._assigned_values = assigned_values
self._contextualized_key = contextualized_key self._contextualized_key = contextualized_key
+11 -17
View File
@@ -1,6 +1,5 @@
from parso.python import tree from parso.python import tree
from jedi._compatibility import use_metaclass
from jedi import debug from jedi import debug
from jedi.inference.cache import inference_state_method_cache, CachedMetaClass from jedi.inference.cache import inference_state_method_cache, CachedMetaClass
from jedi.inference import compiled from jedi.inference import compiled
@@ -26,7 +25,7 @@ from jedi.inference.gradual.generics import TupleGenericManager
class LambdaName(AbstractNameDefinition): class LambdaName(AbstractNameDefinition):
string_name = '<lambda>' string_name = '<lambda>'
api_type = u'function' api_type = 'function'
def __init__(self, lambda_value): def __init__(self, lambda_value):
self._lambda_value = lambda_value self._lambda_value = lambda_value
@@ -55,7 +54,7 @@ class FunctionAndClassBase(TreeValue):
class FunctionMixin(object): class FunctionMixin(object):
api_type = u'function' api_type = 'function'
def get_filters(self, origin_scope=None): def get_filters(self, origin_scope=None):
cls = self.py__class__() cls = self.py__class__()
@@ -126,7 +125,7 @@ class FunctionMixin(object):
return [TreeSignature(f) for f in self.get_signature_functions()] return [TreeSignature(f) for f in self.get_signature_functions()]
class FunctionValue(use_metaclass(CachedMetaClass, FunctionMixin, FunctionAndClassBase)): class FunctionValue(FunctionMixin, FunctionAndClassBase, metaclass=CachedMetaClass):
@classmethod @classmethod
def from_context(cls, context, tree_node): def from_context(cls, context, tree_node):
def create(tree_node): def create(tree_node):
@@ -161,7 +160,7 @@ class FunctionValue(use_metaclass(CachedMetaClass, FunctionMixin, FunctionAndCla
return function return function
def py__class__(self): def py__class__(self):
c, = values_from_qualified_names(self.inference_state, u'types', u'FunctionType') c, = values_from_qualified_names(self.inference_state, 'types', 'FunctionType')
return c return c
def get_default_param_context(self): def get_default_param_context(self):
@@ -173,7 +172,7 @@ class FunctionValue(use_metaclass(CachedMetaClass, FunctionMixin, FunctionAndCla
class FunctionNameInClass(NameWrapper): class FunctionNameInClass(NameWrapper):
def __init__(self, class_context, name): def __init__(self, class_context, name):
super(FunctionNameInClass, self).__init__(name) super().__init__(name)
self._class_context = class_context self._class_context = class_context
def get_defining_qualified_value(self): def get_defining_qualified_value(self):
@@ -182,7 +181,7 @@ class FunctionNameInClass(NameWrapper):
class MethodValue(FunctionValue): class MethodValue(FunctionValue):
def __init__(self, inference_state, class_context, *args, **kwargs): def __init__(self, inference_state, class_context, *args, **kwargs):
super(MethodValue, self).__init__(inference_state, *args, **kwargs) super().__init__(inference_state, *args, **kwargs)
self.class_context = class_context self.class_context = class_context
def get_default_param_context(self): def get_default_param_context(self):
@@ -198,7 +197,7 @@ class MethodValue(FunctionValue):
@property @property
def name(self): def name(self):
return FunctionNameInClass(self.class_context, super(MethodValue, self).name) return FunctionNameInClass(self.class_context, super().name)
class BaseFunctionExecutionContext(ValueContext, TreeContextMixin): class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
@@ -238,7 +237,7 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
try: try:
children = r.children children = r.children
except AttributeError: except AttributeError:
ctx = compiled.builtin_from_name(self.inference_state, u'None') ctx = compiled.builtin_from_name(self.inference_state, 'None')
value_set |= ValueSet([ctx]) value_set |= ValueSet([ctx])
else: else:
value_set |= self.infer_node(children[1]) value_set |= self.infer_node(children[1])
@@ -250,7 +249,7 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
def _get_yield_lazy_value(self, yield_expr): def _get_yield_lazy_value(self, yield_expr):
if yield_expr.type == 'keyword': if yield_expr.type == 'keyword':
# `yield` just yields None. # `yield` just yields None.
ctx = compiled.builtin_from_name(self.inference_state, u'None') ctx = compiled.builtin_from_name(self.inference_state, 'None')
yield LazyKnownValue(ctx) yield LazyKnownValue(ctx)
return return
@@ -330,8 +329,6 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
if is_coroutine: if is_coroutine:
if self.is_generator(): if self.is_generator():
if inference_state.environment.version_info < (3, 6):
return NO_VALUES
async_generator_classes = inference_state.typing_module \ async_generator_classes = inference_state.typing_module \
.py__getattribute__('AsyncGenerator') .py__getattribute__('AsyncGenerator')
@@ -339,13 +336,10 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
# The contravariant doesn't seem to be defined. # The contravariant doesn't seem to be defined.
generics = (yield_values.py__class__(), NO_VALUES) generics = (yield_values.py__class__(), NO_VALUES)
return ValueSet( return ValueSet(
# In Python 3.6 AsyncGenerator is still a class.
GenericClass(c, TupleGenericManager(generics)) GenericClass(c, TupleGenericManager(generics))
for c in async_generator_classes for c in async_generator_classes
).execute_annotation() ).execute_annotation()
else: else:
if inference_state.environment.version_info < (3, 5):
return NO_VALUES
async_classes = inference_state.typing_module.py__getattribute__('Coroutine') async_classes = inference_state.typing_module.py__getattribute__('Coroutine')
return_values = self.get_return_values() return_values = self.get_return_values()
# Only the first generic is relevant. # Only the first generic is relevant.
@@ -362,7 +356,7 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
class FunctionExecutionContext(BaseFunctionExecutionContext): class FunctionExecutionContext(BaseFunctionExecutionContext):
def __init__(self, function_value, arguments): def __init__(self, function_value, arguments):
super(FunctionExecutionContext, self).__init__(function_value) super().__init__(function_value)
self._arguments = arguments self._arguments = arguments
def get_filters(self, until_position=None, origin_scope=None): def get_filters(self, until_position=None, origin_scope=None):
@@ -403,7 +397,7 @@ class AnonymousFunctionExecution(BaseFunctionExecutionContext):
class OverloadedFunctionValue(FunctionMixin, ValueWrapper): class OverloadedFunctionValue(FunctionMixin, ValueWrapper):
def __init__(self, function, overloaded_functions): def __init__(self, function, overloaded_functions):
super(OverloadedFunctionValue, self).__init__(function) super().__init__(function)
self._overloaded_functions = overloaded_functions self._overloaded_functions = overloaded_functions
def py__call__(self, arguments): def py__call__(self, arguments):
+29 -34
View File
@@ -25,7 +25,7 @@ from jedi.parser_utils import function_is_staticmethod, function_is_classmethod
class InstanceExecutedParamName(ParamName): class InstanceExecutedParamName(ParamName):
def __init__(self, instance, function_value, tree_name): def __init__(self, instance, function_value, tree_name):
super(InstanceExecutedParamName, self).__init__( super().__init__(
function_value, tree_name, arguments=None) function_value, tree_name, arguments=None)
self._instance = instance self._instance = instance
@@ -38,7 +38,7 @@ class InstanceExecutedParamName(ParamName):
class AnonymousMethodExecutionFilter(AnonymousFunctionExecutionFilter): class AnonymousMethodExecutionFilter(AnonymousFunctionExecutionFilter):
def __init__(self, instance, *args, **kwargs): def __init__(self, instance, *args, **kwargs):
super(AnonymousMethodExecutionFilter, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._instance = instance self._instance = instance
def _convert_param(self, param, name): def _convert_param(self, param, name):
@@ -55,12 +55,12 @@ class AnonymousMethodExecutionFilter(AnonymousFunctionExecutionFilter):
self._function_value, self._function_value,
name name
) )
return super(AnonymousMethodExecutionFilter, self)._convert_param(param, name) return super()._convert_param(param, name)
class AnonymousMethodExecutionContext(BaseFunctionExecutionContext): class AnonymousMethodExecutionContext(BaseFunctionExecutionContext):
def __init__(self, instance, value): def __init__(self, instance, value):
super(AnonymousMethodExecutionContext, self).__init__(value) super().__init__(value)
self.instance = instance self.instance = instance
def get_filters(self, until_position=None, origin_scope=None): def get_filters(self, until_position=None, origin_scope=None):
@@ -83,15 +83,15 @@ class AnonymousMethodExecutionContext(BaseFunctionExecutionContext):
class MethodExecutionContext(FunctionExecutionContext): class MethodExecutionContext(FunctionExecutionContext):
def __init__(self, instance, *args, **kwargs): def __init__(self, instance, *args, **kwargs):
super(MethodExecutionContext, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.instance = instance self.instance = instance
class AbstractInstanceValue(Value): class AbstractInstanceValue(Value):
api_type = u'instance' api_type = 'instance'
def __init__(self, inference_state, parent_context, class_value): def __init__(self, inference_state, parent_context, class_value):
super(AbstractInstanceValue, self).__init__(inference_state, parent_context) super().__init__(inference_state, parent_context)
# Generated instances are classes that are just generated by self # Generated instances are classes that are just generated by self
# (No arguments) used. # (No arguments) used.
self.class_value = class_value self.class_value = class_value
@@ -141,8 +141,7 @@ class CompiledInstance(AbstractInstanceValue):
# This is not really a compiled class, it's just an instance from a # This is not really a compiled class, it's just an instance from a
# compiled class. # compiled class.
def __init__(self, inference_state, parent_context, class_value, arguments): def __init__(self, inference_state, parent_context, class_value, arguments):
super(CompiledInstance, self).__init__(inference_state, parent_context, super().__init__(inference_state, parent_context, class_value)
class_value)
self._arguments = arguments self._arguments = arguments
def get_filters(self, origin_scope=None, include_self_names=True): def get_filters(self, origin_scope=None, include_self_names=True):
@@ -234,14 +233,14 @@ class _BaseTreeInstance(AbstractInstanceValue):
# other way around. # other way around.
if is_big_annoying_library(self.parent_context): if is_big_annoying_library(self.parent_context):
return NO_VALUES return NO_VALUES
names = (self.get_function_slot_names(u'__getattr__') names = (self.get_function_slot_names('__getattr__')
or self.get_function_slot_names(u'__getattribute__')) or self.get_function_slot_names('__getattribute__'))
return self.execute_function_slots(names, name) return self.execute_function_slots(names, name)
def py__getitem__(self, index_value_set, contextualized_node): def py__getitem__(self, index_value_set, contextualized_node):
names = self.get_function_slot_names(u'__getitem__') names = self.get_function_slot_names('__getitem__')
if not names: if not names:
return super(_BaseTreeInstance, self).py__getitem__( return super().py__getitem__(
index_value_set, index_value_set,
contextualized_node, contextualized_node,
) )
@@ -250,9 +249,9 @@ class _BaseTreeInstance(AbstractInstanceValue):
return ValueSet.from_sets(name.infer().execute(args) for name in names) return ValueSet.from_sets(name.infer().execute(args) for name in names)
def py__iter__(self, contextualized_node=None): def py__iter__(self, contextualized_node=None):
iter_slot_names = self.get_function_slot_names(u'__iter__') iter_slot_names = self.get_function_slot_names('__iter__')
if not iter_slot_names: if not iter_slot_names:
return super(_BaseTreeInstance, self).py__iter__(contextualized_node) return super().py__iter__(contextualized_node)
def iterate(): def iterate():
for generator in self.execute_function_slots(iter_slot_names): for generator in self.execute_function_slots(iter_slot_names):
@@ -261,11 +260,7 @@ class _BaseTreeInstance(AbstractInstanceValue):
return iterate() return iterate()
def py__next__(self, contextualized_node=None): def py__next__(self, contextualized_node=None):
# `__next__` logic. name = u'__next__'
if self.inference_state.environment.version_info.major == 2:
name = u'next'
else:
name = u'__next__'
next_slot_names = self.get_function_slot_names(name) next_slot_names = self.get_function_slot_names(name)
if next_slot_names: if next_slot_names:
yield LazyKnownValues( yield LazyKnownValues(
@@ -275,10 +270,10 @@ class _BaseTreeInstance(AbstractInstanceValue):
debug.warning('Instance has no __next__ function in %s.', self) debug.warning('Instance has no __next__ function in %s.', self)
def py__call__(self, arguments): def py__call__(self, arguments):
names = self.get_function_slot_names(u'__call__') names = self.get_function_slot_names('__call__')
if not names: if not names:
# Means the Instance is not callable. # Means the Instance is not callable.
return super(_BaseTreeInstance, self).py__call__(arguments) return super().py__call__(arguments)
return ValueSet.from_sets(name.infer().execute(arguments) for name in names) return ValueSet.from_sets(name.infer().execute(arguments) for name in names)
@@ -293,10 +288,10 @@ class _BaseTreeInstance(AbstractInstanceValue):
if result is not NotImplemented: if result is not NotImplemented:
return result return result
names = self.get_function_slot_names(u'__get__') names = self.get_function_slot_names('__get__')
if names: if names:
if instance is None: if instance is None:
instance = compiled.builtin_from_name(self.inference_state, u'None') instance = compiled.builtin_from_name(self.inference_state, 'None')
return self.execute_function_slots(names, instance, class_value) return self.execute_function_slots(names, instance, class_value)
else: else:
return ValueSet([self]) return ValueSet([self])
@@ -322,7 +317,7 @@ class TreeInstance(_BaseTreeInstance):
if settings.dynamic_array_additions: if settings.dynamic_array_additions:
arguments = get_dynamic_array_instance(self, arguments) arguments = get_dynamic_array_instance(self, arguments)
super(TreeInstance, self).__init__(inference_state, parent_context, class_value) super().__init__(inference_state, parent_context, class_value)
self._arguments = arguments self._arguments = arguments
self.tree_node = class_value.tree_node self.tree_node = class_value.tree_node
@@ -396,7 +391,7 @@ class TreeInstance(_BaseTreeInstance):
else: else:
if key == index: if key == index:
return lazy_context.infer() return lazy_context.infer()
return super(TreeInstance, self).py__simple_getitem__(index) return super().py__simple_getitem__(index)
def __repr__(self): def __repr__(self):
return "<%s of %s(%s)>" % (self.__class__.__name__, self.class_value, return "<%s of %s(%s)>" % (self.__class__.__name__, self.class_value,
@@ -411,7 +406,7 @@ class CompiledInstanceName(compiled.CompiledName):
def __init__(self, inference_state, instance, klass, name): def __init__(self, inference_state, instance, klass, name):
parent_value = klass.parent_context.get_value() parent_value = klass.parent_context.get_value()
assert parent_value is not None, "How? Please reproduce and report" assert parent_value is not None, "How? Please reproduce and report"
super(CompiledInstanceName, self).__init__( super().__init__(
inference_state, inference_state,
parent_value, parent_value,
name.string_name name.string_name
@@ -449,7 +444,7 @@ class CompiledInstanceClassFilter(AbstractFilter):
class BoundMethod(FunctionMixin, ValueWrapper): class BoundMethod(FunctionMixin, ValueWrapper):
def __init__(self, instance, class_context, function): def __init__(self, instance, class_context, function):
super(BoundMethod, self).__init__(function) super().__init__(function)
self.instance = instance self.instance = instance
self._class_context = class_context self._class_context = class_context
@@ -460,11 +455,11 @@ class BoundMethod(FunctionMixin, ValueWrapper):
def name(self): def name(self):
return FunctionNameInClass( return FunctionNameInClass(
self._class_context, self._class_context,
super(BoundMethod, self).name super().name
) )
def py__class__(self): def py__class__(self):
c, = values_from_qualified_names(self.inference_state, u'types', u'MethodType') c, = values_from_qualified_names(self.inference_state, 'types', 'MethodType')
return c return c
def _get_arguments(self, arguments): def _get_arguments(self, arguments):
@@ -492,7 +487,7 @@ class BoundMethod(FunctionMixin, ValueWrapper):
] ]
def get_signatures(self): def get_signatures(self):
return [sig.bind(self) for sig in super(BoundMethod, self).get_signatures()] return [sig.bind(self) for sig in super().get_signatures()]
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self._wrapped_value) return '<%s: %s>' % (self.__class__.__name__, self._wrapped_value)
@@ -525,7 +520,7 @@ class SelfName(TreeNameDefinition):
class LazyInstanceClassName(NameWrapper): class LazyInstanceClassName(NameWrapper):
def __init__(self, instance, class_member_name): def __init__(self, instance, class_member_name):
super(LazyInstanceClassName, self).__init__(class_member_name) super().__init__(class_member_name)
self._instance = instance self._instance = instance
@iterator_to_value_set @iterator_to_value_set
@@ -572,7 +567,7 @@ class SelfAttributeFilter(ClassFilter):
This class basically filters all the use cases where `self.*` was assigned. This class basically filters all the use cases where `self.*` was assigned.
""" """
def __init__(self, instance, instance_class, node_context, origin_scope): def __init__(self, instance, instance_class, node_context, origin_scope):
super(SelfAttributeFilter, self).__init__( super().__init__(
class_value=instance_class, class_value=instance_class,
node_context=node_context, node_context=node_context,
origin_scope=origin_scope, origin_scope=origin_scope,
@@ -616,7 +611,7 @@ class SelfAttributeFilter(ClassFilter):
class InstanceArguments(TreeArgumentsWrapper): class InstanceArguments(TreeArgumentsWrapper):
def __init__(self, instance, arguments): def __init__(self, instance, arguments):
super(InstanceArguments, self).__init__(arguments) super().__init__(arguments)
self.instance = instance self.instance = instance
def unpack(self, func=None): def unpack(self, func=None):
+29 -53
View File
@@ -2,9 +2,6 @@
Contains all classes and functions to deal with lists, dicts, generators and Contains all classes and functions to deal with lists, dicts, generators and
iterators in general. iterators in general.
""" """
import sys
from jedi._compatibility import force_unicode, is_py3
from jedi.inference import compiled from jedi.inference import compiled
from jedi.inference import analysis from jedi.inference import analysis
from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \ from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \
@@ -27,7 +24,7 @@ class IterableMixin(object):
return self.py__iter__(contextualized_node) return self.py__iter__(contextualized_node)
def py__stop_iteration_returns(self): def py__stop_iteration_returns(self):
return ValueSet([compiled.builtin_from_name(self.inference_state, u'None')]) return ValueSet([compiled.builtin_from_name(self.inference_state, 'None')])
# At the moment, safe values are simple values like "foo", 1 and not # At the moment, safe values are simple values like "foo", 1 and not
# lists/dicts. Therefore as a small speed optimization we can just do the # lists/dicts. Therefore as a small speed optimization we can just do the
@@ -35,14 +32,7 @@ class IterableMixin(object):
# doing this in the end as well. # doing this in the end as well.
# This mostly speeds up patterns like `sys.version_info >= (3, 0)` in # This mostly speeds up patterns like `sys.version_info >= (3, 0)` in
# typeshed. # typeshed.
if sys.version_info[0] == 2: get_safe_value = Value.get_safe_value
# Python 2...........
def get_safe_value(self, default=sentinel):
if default is sentinel:
raise ValueError("There exists no safe value for value %s" % self)
return default
else:
get_safe_value = Value.get_safe_value
class GeneratorBase(LazyAttributeOverwrite, IterableMixin): class GeneratorBase(LazyAttributeOverwrite, IterableMixin):
@@ -64,13 +54,12 @@ class GeneratorBase(LazyAttributeOverwrite, IterableMixin):
return ValueSet([self]) return ValueSet([self])
@publish_method('send') @publish_method('send')
@publish_method('next', python_version_match=2) @publish_method('__next__')
@publish_method('__next__', python_version_match=3)
def _next(self, arguments): def _next(self, arguments):
return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__()) return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__())
def py__stop_iteration_returns(self): def py__stop_iteration_returns(self):
return ValueSet([compiled.builtin_from_name(self.inference_state, u'None')]) return ValueSet([compiled.builtin_from_name(self.inference_state, 'None')])
@property @property
def name(self): def name(self):
@@ -86,7 +75,7 @@ class GeneratorBase(LazyAttributeOverwrite, IterableMixin):
class Generator(GeneratorBase): class Generator(GeneratorBase):
"""Handling of `yield` functions.""" """Handling of `yield` functions."""
def __init__(self, inference_state, func_execution_context): def __init__(self, inference_state, func_execution_context):
super(Generator, self).__init__(inference_state) super().__init__(inference_state)
self._func_execution_context = func_execution_context self._func_execution_context = func_execution_context
def py__iter__(self, contextualized_node=None): def py__iter__(self, contextualized_node=None):
@@ -194,7 +183,7 @@ class _DictMixin(object):
class Sequence(LazyAttributeOverwrite, IterableMixin): class Sequence(LazyAttributeOverwrite, IterableMixin):
api_type = u'instance' api_type = 'instance'
@property @property
def name(self): def name(self):
@@ -233,14 +222,14 @@ class Sequence(LazyAttributeOverwrite, IterableMixin):
class _BaseComprehension(ComprehensionMixin): class _BaseComprehension(ComprehensionMixin):
def __init__(self, inference_state, defining_context, sync_comp_for_node, entry_node): def __init__(self, inference_state, defining_context, sync_comp_for_node, entry_node):
assert sync_comp_for_node.type == 'sync_comp_for' assert sync_comp_for_node.type == 'sync_comp_for'
super(_BaseComprehension, self).__init__(inference_state) super().__init__(inference_state)
self._defining_context = defining_context self._defining_context = defining_context
self._sync_comp_for_node = sync_comp_for_node self._sync_comp_for_node = sync_comp_for_node
self._entry_node = entry_node self._entry_node = entry_node
class ListComprehension(_BaseComprehension, Sequence): class ListComprehension(_BaseComprehension, Sequence):
array_type = u'list' array_type = 'list'
def py__simple_getitem__(self, index): def py__simple_getitem__(self, index):
if isinstance(index, slice): if isinstance(index, slice):
@@ -253,7 +242,7 @@ class ListComprehension(_BaseComprehension, Sequence):
class SetComprehension(_BaseComprehension, Sequence): class SetComprehension(_BaseComprehension, Sequence):
array_type = u'set' array_type = 'set'
class GeneratorComprehension(_BaseComprehension, GeneratorBase): class GeneratorComprehension(_BaseComprehension, GeneratorBase):
@@ -271,11 +260,11 @@ class _DictKeyMixin(object):
class DictComprehension(ComprehensionMixin, Sequence, _DictKeyMixin): class DictComprehension(ComprehensionMixin, Sequence, _DictKeyMixin):
array_type = u'dict' array_type = 'dict'
def __init__(self, inference_state, defining_context, sync_comp_for_node, key_node, value_node): def __init__(self, inference_state, defining_context, sync_comp_for_node, key_node, value_node):
assert sync_comp_for_node.type == 'sync_comp_for' assert sync_comp_for_node.type == 'sync_comp_for'
super(DictComprehension, self).__init__(inference_state) super().__init__(inference_state)
self._defining_context = defining_context self._defining_context = defining_context
self._sync_comp_for_node = sync_comp_for_node self._sync_comp_for_node = sync_comp_for_node
self._entry_node = key_node self._entry_node = key_node
@@ -328,25 +317,25 @@ class DictComprehension(ComprehensionMixin, Sequence, _DictKeyMixin):
class SequenceLiteralValue(Sequence): class SequenceLiteralValue(Sequence):
_TUPLE_LIKE = 'testlist_star_expr', 'testlist', 'subscriptlist' _TUPLE_LIKE = 'testlist_star_expr', 'testlist', 'subscriptlist'
mapping = {'(': u'tuple', mapping = {'(': 'tuple',
'[': u'list', '[': 'list',
'{': u'set'} '{': 'set'}
def __init__(self, inference_state, defining_context, atom): def __init__(self, inference_state, defining_context, atom):
super(SequenceLiteralValue, self).__init__(inference_state) super().__init__(inference_state)
self.atom = atom self.atom = atom
self._defining_context = defining_context self._defining_context = defining_context
if self.atom.type in self._TUPLE_LIKE: if self.atom.type in self._TUPLE_LIKE:
self.array_type = u'tuple' self.array_type = 'tuple'
else: else:
self.array_type = SequenceLiteralValue.mapping[atom.children[0]] self.array_type = SequenceLiteralValue.mapping[atom.children[0]]
"""The builtin name of the array (list, set, tuple or dict).""" """The builtin name of the array (list, set, tuple or dict)."""
def _get_generics(self): def _get_generics(self):
if self.array_type == u'tuple': if self.array_type == 'tuple':
return tuple(x.infer().py__class__() for x in self.py__iter__()) return tuple(x.infer().py__class__() for x in self.py__iter__())
return super(SequenceLiteralValue, self)._get_generics() return super()._get_generics()
def py__simple_getitem__(self, index): def py__simple_getitem__(self, index):
"""Here the index is an int/str. Raises IndexError/KeyError.""" """Here the index is an int/str. Raises IndexError/KeyError."""
@@ -436,10 +425,12 @@ class SequenceLiteralValue(Sequence):
class DictLiteralValue(_DictMixin, SequenceLiteralValue, _DictKeyMixin): class DictLiteralValue(_DictMixin, SequenceLiteralValue, _DictKeyMixin):
array_type = u'dict' array_type = 'dict'
def __init__(self, inference_state, defining_context, atom): def __init__(self, inference_state, defining_context, atom):
super(SequenceLiteralValue, self).__init__(inference_state) # Intentionally don't call the super class. This is definitely a sign
# that the architecture is bad and we should refactor.
Sequence.__init__(self, inference_state)
self._defining_context = defining_context self._defining_context = defining_context
self.atom = atom self.atom = atom
@@ -448,7 +439,7 @@ class DictLiteralValue(_DictMixin, SequenceLiteralValue, _DictKeyMixin):
compiled_value_index = compiled.create_simple_object(self.inference_state, index) compiled_value_index = compiled.create_simple_object(self.inference_state, index)
for key, value in self.get_tree_entries(): for key, value in self.get_tree_entries():
for k in self._defining_context.infer_node(key): for k in self._defining_context.infer_node(key):
for key_v in k.execute_operation(compiled_value_index, u'=='): for key_v in k.execute_operation(compiled_value_index, '=='):
if key_v.get_safe_value(): if key_v.get_safe_value():
return self._defining_context.infer_node(value) return self._defining_context.infer_node(value)
raise SimpleGetItemNotFound('No key found in dictionary %s.' % self) raise SimpleGetItemNotFound('No key found in dictionary %s.' % self)
@@ -502,7 +493,7 @@ class _FakeSequence(Sequence):
""" """
type should be one of "tuple", "list" type should be one of "tuple", "list"
""" """
super(_FakeSequence, self).__init__(inference_state) super().__init__(inference_state)
self._lazy_value_list = lazy_value_list self._lazy_value_list = lazy_value_list
def py__simple_getitem__(self, index): def py__simple_getitem__(self, index):
@@ -524,18 +515,18 @@ class _FakeSequence(Sequence):
class FakeTuple(_FakeSequence): class FakeTuple(_FakeSequence):
array_type = u'tuple' array_type = 'tuple'
class FakeList(_FakeSequence): class FakeList(_FakeSequence):
array_type = u'tuple' array_type = 'tuple'
class FakeDict(_DictMixin, Sequence, _DictKeyMixin): class FakeDict(_DictMixin, Sequence, _DictKeyMixin):
array_type = u'dict' array_type = 'dict'
def __init__(self, inference_state, dct): def __init__(self, inference_state, dct):
super(FakeDict, self).__init__(inference_state) super().__init__(inference_state)
self._dct = dct self._dct = dct
def py__iter__(self, contextualized_node=None): def py__iter__(self, contextualized_node=None):
@@ -543,21 +534,6 @@ class FakeDict(_DictMixin, Sequence, _DictKeyMixin):
yield LazyKnownValue(compiled.create_simple_object(self.inference_state, key)) yield LazyKnownValue(compiled.create_simple_object(self.inference_state, key))
def py__simple_getitem__(self, index): def py__simple_getitem__(self, index):
if is_py3 and self.inference_state.environment.version_info.major == 2:
# In Python 2 bytes and unicode compare.
if isinstance(index, bytes):
index_unicode = force_unicode(index)
try:
return self._dct[index_unicode].infer()
except KeyError:
pass
elif isinstance(index, str):
index_bytes = index.encode('utf-8')
try:
return self._dct[index_bytes].infer()
except KeyError:
pass
with reraise_getitem_errors(KeyError, TypeError): with reraise_getitem_errors(KeyError, TypeError):
lazy_value = self._dct[index] lazy_value = self._dct[index]
return lazy_value.infer() return lazy_value.infer()
@@ -584,7 +560,7 @@ class FakeDict(_DictMixin, Sequence, _DictKeyMixin):
class MergedArray(Sequence): class MergedArray(Sequence):
def __init__(self, inference_state, arrays): def __init__(self, inference_state, arrays):
super(MergedArray, self).__init__(inference_state) super().__init__(inference_state)
self.array_type = arrays[-1].array_type self.array_type = arrays[-1].array_type
self._arrays = arrays self._arrays = arrays
+9 -12
View File
@@ -37,7 +37,6 @@ py__doc__() Returns the docstring for a value.
""" """
from jedi import debug from jedi import debug
from jedi._compatibility import use_metaclass
from jedi.parser_utils import get_cached_parent_scope, expr_is_dotted from jedi.parser_utils import get_cached_parent_scope, expr_is_dotted
from jedi.inference.cache import inference_state_method_cache, CachedMetaClass, \ from jedi.inference.cache import inference_state_method_cache, CachedMetaClass, \
inference_state_method_generator_cache inference_state_method_generator_cache
@@ -56,7 +55,7 @@ from jedi.plugins import plugin_manager
class ClassName(TreeNameDefinition): class ClassName(TreeNameDefinition):
def __init__(self, class_value, tree_name, name_context, apply_decorators): def __init__(self, class_value, tree_name, name_context, apply_decorators):
super(ClassName, self).__init__(name_context, tree_name) super().__init__(name_context, tree_name)
self._apply_decorators = apply_decorators self._apply_decorators = apply_decorators
self._class_value = class_value self._class_value = class_value
@@ -78,7 +77,7 @@ class ClassName(TreeNameDefinition):
class ClassFilter(ParserTreeFilter): class ClassFilter(ParserTreeFilter):
def __init__(self, class_value, node_context=None, until_position=None, def __init__(self, class_value, node_context=None, until_position=None,
origin_scope=None, is_instance=False): origin_scope=None, is_instance=False):
super(ClassFilter, self).__init__( super().__init__(
class_value.as_context(), node_context, class_value.as_context(), node_context,
until_position=until_position, until_position=until_position,
origin_scope=origin_scope, origin_scope=origin_scope,
@@ -125,7 +124,7 @@ class ClassFilter(ParserTreeFilter):
or self._equals_origin_scope() or self._equals_origin_scope()
def _filter(self, names): def _filter(self, names):
names = super(ClassFilter, self)._filter(names) names = super()._filter(names)
return [name for name in names if self._access_possible(name)] return [name for name in names if self._access_possible(name)]
@@ -145,7 +144,7 @@ class ClassMixin(object):
return ValueSet([TreeInstance(self.inference_state, self.parent_context, self, arguments)]) return ValueSet([TreeInstance(self.inference_state, self.parent_context, self, arguments)])
def py__class__(self): def py__class__(self):
return compiled.builtin_from_name(self.inference_state, u'type') return compiled.builtin_from_name(self.inference_state, 'type')
@property @property
def name(self): def name(self):
@@ -192,13 +191,11 @@ class ClassMixin(object):
if include_metaclasses: if include_metaclasses:
metaclasses = self.get_metaclasses() metaclasses = self.get_metaclasses()
if metaclasses: if metaclasses:
for f in self.get_metaclass_filters(metaclasses, is_instance): yield from self.get_metaclass_filters(metaclasses, is_instance)
yield f # Python 2..
for cls in self.py__mro__(): for cls in self.py__mro__():
if cls.is_compiled(): if cls.is_compiled():
for filter in cls.get_filters(is_instance=is_instance): yield from cls.get_filters(is_instance=is_instance)
yield filter
else: else:
yield ClassFilter( yield ClassFilter(
self, node_context=cls.as_context(), self, node_context=cls.as_context(),
@@ -207,7 +204,7 @@ class ClassMixin(object):
) )
if not is_instance and include_type_when_class: if not is_instance and include_type_when_class:
from jedi.inference.compiled import builtin_from_name from jedi.inference.compiled import builtin_from_name
type_ = builtin_from_name(self.inference_state, u'type') type_ = builtin_from_name(self.inference_state, 'type')
assert isinstance(type_, ClassValue) assert isinstance(type_, ClassValue)
if type_ != self: if type_ != self:
# We are not using execute_with_values here, because the # We are not using execute_with_values here, because the
@@ -321,8 +318,8 @@ class ClassMixin(object):
return ValueSet({self}) return ValueSet({self})
class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase)): class ClassValue(ClassMixin, FunctionAndClassBase, metaclass=CachedMetaClass):
api_type = u'class' api_type = 'class'
@inference_state_method_cache() @inference_state_method_cache()
def list_type_vars(self): def list_type_vars(self):
+13 -15
View File
@@ -1,4 +1,5 @@
import os import os
from pathlib import Path
from jedi.inference.cache import inference_state_method_cache from jedi.inference.cache import inference_state_method_cache
from jedi.inference.names import AbstractNameDefinition, ModuleName from jedi.inference.names import AbstractNameDefinition, ModuleName
@@ -16,7 +17,7 @@ class _ModuleAttributeName(AbstractNameDefinition):
""" """
For module attributes like __file__, __str__ and so on. For module attributes like __file__, __str__ and so on.
""" """
api_type = u'instance' api_type = 'instance'
def __init__(self, parent_module, string_name, string_value=None): def __init__(self, parent_module, string_name, string_value=None):
self.parent_context = parent_module self.parent_context = parent_module
@@ -26,9 +27,6 @@ class _ModuleAttributeName(AbstractNameDefinition):
def infer(self): def infer(self):
if self._string_value is not None: if self._string_value is not None:
s = self._string_value s = self._string_value
if self.parent_context.inference_state.environment.version_info.major == 2 \
and not isinstance(s, bytes):
s = s.encode('utf-8')
return ValueSet([ return ValueSet([
create_simple_object(self.parent_context.inference_state, s) create_simple_object(self.parent_context.inference_state, s)
]) ])
@@ -73,7 +71,7 @@ class ModuleMixin(SubModuleDictMixin):
yield star_filter yield star_filter
def py__class__(self): def py__class__(self):
c, = values_from_qualified_names(self.inference_state, u'types', u'ModuleType') c, = values_from_qualified_names(self.inference_state, 'types', 'ModuleType')
return c return c
def is_module(self): def is_module(self):
@@ -92,9 +90,9 @@ class ModuleMixin(SubModuleDictMixin):
names = ['__package__', '__doc__', '__name__'] names = ['__package__', '__doc__', '__name__']
# All the additional module attributes are strings. # All the additional module attributes are strings.
dct = dict((n, _ModuleAttributeName(self, n)) for n in names) dct = dict((n, _ModuleAttributeName(self, n)) for n in names)
file = self.py__file__() path = self.py__file__()
if file is not None: if path is not None:
dct['__file__'] = _ModuleAttributeName(self, '__file__', file) dct['__file__'] = _ModuleAttributeName(self, '__file__', str(path))
return dct return dct
def iter_star_filters(self): def iter_star_filters(self):
@@ -137,11 +135,11 @@ class ModuleMixin(SubModuleDictMixin):
class ModuleValue(ModuleMixin, TreeValue): class ModuleValue(ModuleMixin, TreeValue):
api_type = u'module' api_type = 'module'
def __init__(self, inference_state, module_node, code_lines, file_io=None, def __init__(self, inference_state, module_node, code_lines, file_io=None,
string_names=None, is_package=False): string_names=None, is_package=False):
super(ModuleValue, self).__init__( super().__init__(
inference_state, inference_state,
parent_context=None, parent_context=None,
tree_node=module_node tree_node=module_node
@@ -150,32 +148,32 @@ class ModuleValue(ModuleMixin, TreeValue):
if file_io is None: if file_io is None:
self._path = None self._path = None
else: else:
self._path = file_io.path self._path = Path(file_io.path)
self.string_names = string_names # Optional[Tuple[str, ...]] self.string_names = string_names # Optional[Tuple[str, ...]]
self.code_lines = code_lines self.code_lines = code_lines
self._is_package = is_package self._is_package = is_package
def is_stub(self): def is_stub(self):
if self._path is not None and self._path.endswith('.pyi'): if self._path is not None and self._path.suffix == '.pyi':
# Currently this is the way how we identify stubs when e.g. goto is # Currently this is the way how we identify stubs when e.g. goto is
# used in them. This could be changed if stubs would be identified # used in them. This could be changed if stubs would be identified
# sooner and used as StubModuleValue. # sooner and used as StubModuleValue.
return True return True
return super(ModuleValue, self).is_stub() return super().is_stub()
def py__name__(self): def py__name__(self):
if self.string_names is None: if self.string_names is None:
return None return None
return '.'.join(self.string_names) return '.'.join(self.string_names)
def py__file__(self): def py__file__(self) -> Path:
""" """
In contrast to Python's __file__ can be None. In contrast to Python's __file__ can be None.
""" """
if self._path is None: if self._path is None:
return None return None
return os.path.abspath(self._path) return self._path.absolute()
def is_package(self): def is_package(self):
return self._is_package return self._is_package
+2 -2
View File
@@ -23,11 +23,11 @@ class ImplicitNamespaceValue(Value, SubModuleDictMixin):
# Is a module like every other module, because if you import an empty # Is a module like every other module, because if you import an empty
# folder foobar it will be available as an object: # folder foobar it will be available as an object:
# <module 'foobar' (namespace)>. # <module 'foobar' (namespace)>.
api_type = u'module' api_type = 'module'
parent_context = None parent_context = None
def __init__(self, inference_state, string_names, paths): def __init__(self, inference_state, string_names, paths):
super(ImplicitNamespaceValue, self).__init__(inference_state, parent_context=None) super().__init__(inference_state, parent_context=None)
self.inference_state = inference_state self.inference_state = inference_state
self.string_names = string_names self.string_names = string_names
self._paths = paths self._paths = paths
+4 -19
View File
@@ -1,5 +1,6 @@
import re import re
import textwrap import textwrap
from ast import literal_eval
from inspect import cleandoc from inspect import cleandoc
from weakref import WeakKeyDictionary from weakref import WeakKeyDictionary
@@ -7,8 +8,6 @@ from parso.python import tree
from parso.cache import parser_cache from parso.cache import parser_cache
from parso import split_lines from parso import split_lines
from jedi._compatibility import literal_eval, force_unicode
_EXECUTE_NODES = {'funcdef', 'classdef', 'import_from', 'import_name', 'test', _EXECUTE_NODES = {'funcdef', 'classdef', 'import_from', 'import_name', 'test',
'or_test', 'and_test', 'not_test', 'comparison', 'expr', 'or_test', 'and_test', 'not_test', 'comparison', 'expr',
'xor_expr', 'and_expr', 'shift_expr', 'arith_expr', 'xor_expr', 'and_expr', 'shift_expr', 'arith_expr',
@@ -102,10 +101,7 @@ def clean_scope_docstring(scope_node):
# leaves anymore that might be part of the docstring. A # leaves anymore that might be part of the docstring. A
# docstring can also look like this: ``'foo' 'bar' # docstring can also look like this: ``'foo' 'bar'
# Returns a literal cleaned version of the ``Token``. # Returns a literal cleaned version of the ``Token``.
cleaned = cleandoc(safe_literal_eval(node.value)) return cleandoc(safe_literal_eval(node.value))
# Since we want the docstr output to be always unicode, just
# force it.
return force_unicode(cleaned)
return '' return ''
@@ -117,10 +113,7 @@ def find_statement_documentation(tree_node):
if maybe_string.type == 'simple_stmt': if maybe_string.type == 'simple_stmt':
maybe_string = maybe_string.children[0] maybe_string = maybe_string.children[0]
if maybe_string.type == 'string': if maybe_string.type == 'string':
cleaned = cleandoc(safe_literal_eval(maybe_string.value)) return cleandoc(safe_literal_eval(maybe_string.value))
# Since we want the docstr output to be always unicode, just
# force it.
return force_unicode(cleaned)
return '' return ''
@@ -131,15 +124,7 @@ def safe_literal_eval(value):
# manually, but that's right now not implemented. # manually, but that's right now not implemented.
return '' return ''
try: return literal_eval(value)
return literal_eval(value)
except SyntaxError:
# It's possible to create syntax errors with literals like rb'' in
# Python 2. This should not be possible and in that case just return an
# empty string.
# Before Python 3.3 there was a more strict definition in which order
# you could define literals.
return ''
def get_signature(funcdef, width=72, call_string=None, def get_signature(funcdef, width=72, call_string=None,
+7 -6
View File
@@ -1,7 +1,8 @@
""" """
Module is used to infer Django model fields. Module is used to infer Django model fields.
""" """
from jedi._compatibility import Parameter from inspect import Parameter
from jedi import debug from jedi import debug
from jedi.inference.cache import inference_state_function_cache from jedi.inference.cache import inference_state_function_cache
from jedi.inference.base_value import ValueSet, iterator_to_value_set, ValueWrapper from jedi.inference.base_value import ValueSet, iterator_to_value_set, ValueWrapper
@@ -113,7 +114,7 @@ def _infer_field(cls, field_name, is_instance):
class DjangoModelName(NameWrapper): class DjangoModelName(NameWrapper):
def __init__(self, cls, name, is_instance): def __init__(self, cls, name, is_instance):
super(DjangoModelName, self).__init__(name) super().__init__(name)
self._cls = cls self._cls = cls
self._is_instance = is_instance self._is_instance = is_instance
@@ -257,7 +258,7 @@ class GenericFieldWrapper(AttributeOverwrite, ClassMixin):
class DjangoModelSignature(AbstractSignature): class DjangoModelSignature(AbstractSignature):
def __init__(self, value, field_names): def __init__(self, value, field_names):
super(DjangoModelSignature, self).__init__(value) super().__init__(value)
self._field_names = field_names self._field_names = field_names
def get_param_names(self, resolve_stars=False): def get_param_names(self, resolve_stars=False):
@@ -266,7 +267,7 @@ class DjangoModelSignature(AbstractSignature):
class DjangoParamName(BaseTreeParamName): class DjangoParamName(BaseTreeParamName):
def __init__(self, field_name): def __init__(self, field_name):
super(DjangoParamName, self).__init__(field_name.parent_context, field_name.tree_name) super().__init__(field_name.parent_context, field_name.tree_name)
self._field_name = field_name self._field_name = field_name
def get_kind(self): def get_kind(self):
@@ -278,7 +279,7 @@ class DjangoParamName(BaseTreeParamName):
class QuerySetMethodWrapper(ValueWrapper): class QuerySetMethodWrapper(ValueWrapper):
def __init__(self, method, model_cls): def __init__(self, method, model_cls):
super(QuerySetMethodWrapper, self).__init__(method) super().__init__(method)
self._model_cls = model_cls self._model_cls = model_cls
def py__get__(self, instance, class_value): def py__get__(self, instance, class_value):
@@ -288,7 +289,7 @@ class QuerySetMethodWrapper(ValueWrapper):
class QuerySetBoundMethodWrapper(ValueWrapper): class QuerySetBoundMethodWrapper(ValueWrapper):
def __init__(self, method, model_cls): def __init__(self, method, model_cls):
super(QuerySetBoundMethodWrapper, self).__init__(method) super().__init__(method)
self._model_cls = model_cls self._model_cls = model_cls
def get_signatures(self): def get_signatures(self):
+3 -3
View File
@@ -6,14 +6,14 @@ def import_module(callback):
def wrapper(inference_state, import_names, module_context, *args, **kwargs): def wrapper(inference_state, import_names, module_context, *args, **kwargs):
if len(import_names) == 3 and import_names[:2] == ('flask', 'ext'): if len(import_names) == 3 and import_names[:2] == ('flask', 'ext'):
# New style. # New style.
ipath = (u'flask_' + import_names[2]), ipath = ('flask_' + import_names[2]),
value_set = callback(inference_state, ipath, None, *args, **kwargs) value_set = callback(inference_state, ipath, None, *args, **kwargs)
if value_set: if value_set:
return value_set return value_set
value_set = callback(inference_state, (u'flaskext',), None, *args, **kwargs) value_set = callback(inference_state, ('flaskext',), None, *args, **kwargs)
return callback( return callback(
inference_state, inference_state,
(u'flaskext', import_names[2]), ('flaskext', import_names[2]),
next(iter(value_set)), next(iter(value_set)),
*args, **kwargs *args, **kwargs
) )
+4 -3
View File
@@ -1,5 +1,6 @@
from pathlib import Path
from parso.python.tree import search_ancestor from parso.python.tree import search_ancestor
from jedi._compatibility import FileNotFoundError
from jedi.inference.cache import inference_state_method_cache from jedi.inference.cache import inference_state_method_cache
from jedi.inference.imports import load_module_from_path from jedi.inference.imports import load_module_from_path
from jedi.inference.filters import ParserTreeFilter from jedi.inference.filters import ParserTreeFilter
@@ -129,7 +130,7 @@ def _iter_pytest_modules(module_context, skip_own_module=False):
sys_path = module_context.inference_state.get_sys_path() sys_path = module_context.inference_state.get_sys_path()
while any(folder.path.startswith(p) for p in sys_path): while any(folder.path.startswith(p) for p in sys_path):
file_io = folder.get_file_io('conftest.py') file_io = folder.get_file_io('conftest.py')
if file_io.path != module_context.py__file__(): if Path(file_io.path) != module_context.py__file__():
try: try:
m = load_module_from_path(module_context.inference_state, file_io) m = load_module_from_path(module_context.inference_state, file_io)
yield m.as_context() yield m.as_context()
@@ -144,7 +145,7 @@ def _iter_pytest_modules(module_context, skip_own_module=False):
class FixtureFilter(ParserTreeFilter): class FixtureFilter(ParserTreeFilter):
def _filter(self, names): def _filter(self, names):
for name in super(FixtureFilter, self)._filter(names): for name in super()._filter(names):
funcdef = name.parent funcdef = name.parent
if funcdef.type == 'funcdef': if funcdef.type == 'funcdef':
# Class fixtures are not supported # Class fixtures are not supported
+23 -29
View File
@@ -11,8 +11,8 @@ compiled module that returns the types for C-builtins.
""" """
import parso import parso
import os import os
from inspect import Parameter
from jedi._compatibility import force_unicode, Parameter
from jedi import debug from jedi import debug
from jedi.inference.utils import safe_property from jedi.inference.utils import safe_property
from jedi.inference.helpers import get_str_or_none from jedi.inference.helpers import get_str_or_none
@@ -182,14 +182,9 @@ def argument_clinic(clinic_string, want_value=False, want_context=False,
@argument_clinic('iterator[, default], /', want_inference_state=True) @argument_clinic('iterator[, default], /', want_inference_state=True)
def builtins_next(iterators, defaults, inference_state): def builtins_next(iterators, defaults, inference_state):
if inference_state.environment.version_info.major == 2:
name = 'next'
else:
name = '__next__'
# TODO theoretically we have to check here if something is an iterator. # TODO theoretically we have to check here if something is an iterator.
# That is probably done by checking if it's not a class. # That is probably done by checking if it's not a class.
return defaults | iterators.py__getattribute__(name).execute_with_values() return defaults | iterators.py__getattribute__('__next__').execute_with_values()
@argument_clinic('iterator[, default], /') @argument_clinic('iterator[, default], /')
@@ -208,7 +203,7 @@ def builtins_getattr(objects, names, defaults=None):
debug.warning('getattr called without str') debug.warning('getattr called without str')
continue continue
else: else:
return value.py__getattribute__(force_unicode(string)) return value.py__getattribute__(string)
return NO_VALUES return NO_VALUES
@@ -259,14 +254,13 @@ def builtins_super(types, objects, context):
class ReversedObject(AttributeOverwrite): class ReversedObject(AttributeOverwrite):
def __init__(self, reversed_obj, iter_list): def __init__(self, reversed_obj, iter_list):
super(ReversedObject, self).__init__(reversed_obj) super().__init__(reversed_obj)
self._iter_list = iter_list self._iter_list = iter_list
def py__iter__(self, contextualized_node): def py__iter__(self, contextualized_node):
return self._iter_list return self._iter_list
@publish_method('next', python_version_match=2) @publish_method('__next__')
@publish_method('__next__', python_version_match=3)
def _next(self, arguments): def _next(self, arguments):
return ValueSet.from_sets( return ValueSet.from_sets(
lazy_value.infer() for lazy_value in self._iter_list lazy_value.infer() for lazy_value in self._iter_list
@@ -329,7 +323,7 @@ def builtins_isinstance(objects, types, arguments, inference_state):
analysis.add(lazy_value.context, 'type-error-isinstance', node, message) analysis.add(lazy_value.context, 'type-error-isinstance', node, message)
return ValueSet( return ValueSet(
compiled.builtin_from_name(inference_state, force_unicode(str(b))) compiled.builtin_from_name(inference_state, str(b))
for b in bool_results for b in bool_results
) )
@@ -346,7 +340,7 @@ def builtins_staticmethod(functions):
class ClassMethodObject(ValueWrapper): class ClassMethodObject(ValueWrapper):
def __init__(self, class_method_obj, function): def __init__(self, class_method_obj, function):
super(ClassMethodObject, self).__init__(class_method_obj) super().__init__(class_method_obj)
self._function = function self._function = function
def py__get__(self, instance, class_value): def py__get__(self, instance, class_value):
@@ -358,7 +352,7 @@ class ClassMethodObject(ValueWrapper):
class ClassMethodGet(ValueWrapper): class ClassMethodGet(ValueWrapper):
def __init__(self, get_method, klass, function): def __init__(self, get_method, klass, function):
super(ClassMethodGet, self).__init__(get_method) super().__init__(get_method)
self._class = klass self._class = klass
self._function = function self._function = function
@@ -371,7 +365,7 @@ class ClassMethodGet(ValueWrapper):
class ClassMethodArguments(TreeArgumentsWrapper): class ClassMethodArguments(TreeArgumentsWrapper):
def __init__(self, klass, arguments): def __init__(self, klass, arguments):
super(ClassMethodArguments, self).__init__(arguments) super().__init__(arguments)
self._class = klass self._class = klass
def unpack(self, func=None): def unpack(self, func=None):
@@ -391,7 +385,7 @@ def builtins_classmethod(functions, value, arguments):
class PropertyObject(AttributeOverwrite, ValueWrapper): class PropertyObject(AttributeOverwrite, ValueWrapper):
def __init__(self, property_obj, function): def __init__(self, property_obj, function):
super(PropertyObject, self).__init__(property_obj) super().__init__(property_obj)
self._function = function self._function = function
def py__get__(self, instance, class_value): def py__get__(self, instance, class_value):
@@ -426,11 +420,11 @@ def collections_namedtuple(value, arguments, callback):
inference_state = value.inference_state inference_state = value.inference_state
# Process arguments # Process arguments
name = u'jedi_unknown_namedtuple' name = 'jedi_unknown_namedtuple'
for c in _follow_param(inference_state, arguments, 0): for c in _follow_param(inference_state, arguments, 0):
x = get_str_or_none(c) x = get_str_or_none(c)
if x is not None: if x is not None:
name = force_unicode(x) name = x
break break
# TODO here we only use one of the types, we should use all. # TODO here we only use one of the types, we should use all.
@@ -440,10 +434,10 @@ def collections_namedtuple(value, arguments, callback):
_fields = list(param_values)[0] _fields = list(param_values)[0]
string = get_str_or_none(_fields) string = get_str_or_none(_fields)
if string is not None: if string is not None:
fields = force_unicode(string).replace(',', ' ').split() fields = string.replace(',', ' ').split()
elif isinstance(_fields, iterable.Sequence): elif isinstance(_fields, iterable.Sequence):
fields = [ fields = [
force_unicode(get_str_or_none(v)) get_str_or_none(v)
for lazy_value in _fields.py__iter__() for lazy_value in _fields.py__iter__()
for v in lazy_value.infer() for v in lazy_value.infer()
] ]
@@ -456,7 +450,7 @@ def collections_namedtuple(value, arguments, callback):
typename=name, typename=name,
field_names=tuple(fields), field_names=tuple(fields),
num_fields=len(fields), num_fields=len(fields),
arg_list=repr(tuple(fields)).replace("u'", "").replace("'", "")[1:-1], arg_list=repr(tuple(fields)).replace("'", "")[1:-1],
repr_fmt='', repr_fmt='',
field_defs='\n'.join(_NAMEDTUPLE_FIELD_TEMPLATE.format(index=index, name=name) field_defs='\n'.join(_NAMEDTUPLE_FIELD_TEMPLATE.format(index=index, name=name)
for index, name in enumerate(fields)) for index, name in enumerate(fields))
@@ -475,7 +469,7 @@ def collections_namedtuple(value, arguments, callback):
class PartialObject(ValueWrapper): class PartialObject(ValueWrapper):
def __init__(self, actual_value, arguments, instance=None): def __init__(self, actual_value, arguments, instance=None):
super(PartialObject, self).__init__(actual_value) super().__init__(actual_value)
self._arguments = arguments self._arguments = arguments
self._instance = instance self._instance = instance
@@ -538,7 +532,7 @@ class PartialMethodObject(PartialObject):
class PartialSignature(SignatureWrapper): class PartialSignature(SignatureWrapper):
def __init__(self, wrapped_signature, skipped_arg_count, skipped_arg_set): def __init__(self, wrapped_signature, skipped_arg_count, skipped_arg_set):
super(PartialSignature, self).__init__(wrapped_signature) super().__init__(wrapped_signature)
self._skipped_arg_count = skipped_arg_count self._skipped_arg_count = skipped_arg_count
self._skipped_arg_set = skipped_arg_set self._skipped_arg_set = skipped_arg_set
@@ -631,7 +625,7 @@ class DataclassWrapper(ValueWrapper, ClassMixin):
class DataclassSignature(AbstractSignature): class DataclassSignature(AbstractSignature):
def __init__(self, value, param_names): def __init__(self, value, param_names):
super(DataclassSignature, self).__init__(value) super().__init__(value)
self._param_names = param_names self._param_names = param_names
def get_param_names(self, resolve_stars=False): def get_param_names(self, resolve_stars=False):
@@ -640,7 +634,7 @@ class DataclassSignature(AbstractSignature):
class DataclassParamName(BaseTreeParamName): class DataclassParamName(BaseTreeParamName):
def __init__(self, parent_context, tree_name, annotation_node, default_node): def __init__(self, parent_context, tree_name, annotation_node, default_node):
super(DataclassParamName, self).__init__(parent_context, tree_name) super().__init__(parent_context, tree_name)
self.annotation_node = annotation_node self.annotation_node = annotation_node
self.default_node = default_node self.default_node = default_node
@@ -656,7 +650,7 @@ class DataclassParamName(BaseTreeParamName):
class ItemGetterCallable(ValueWrapper): class ItemGetterCallable(ValueWrapper):
def __init__(self, instance, args_value_set): def __init__(self, instance, args_value_set):
super(ItemGetterCallable, self).__init__(instance) super().__init__(instance)
self._args_value_set = args_value_set self._args_value_set = args_value_set
@repack_with_argument_clinic('item, /') @repack_with_argument_clinic('item, /')
@@ -694,7 +688,7 @@ class WrapsCallable(ValueWrapper):
class Wrapped(ValueWrapper, FunctionMixin): class Wrapped(ValueWrapper, FunctionMixin):
def __init__(self, func, original_function): def __init__(self, func, original_function):
super(Wrapped, self).__init__(func) super().__init__(func)
self._original_function = original_function self._original_function = original_function
@property @property
@@ -732,7 +726,7 @@ def _create_string_input_function(func):
@argument_clinic('*args, /', want_callback=True) @argument_clinic('*args, /', want_callback=True)
def _os_path_join(args_set, callback): def _os_path_join(args_set, callback):
if len(args_set) == 1: if len(args_set) == 1:
string = u'' string = ''
sequence, = args_set sequence, = args_set
is_first = True is_first = True
for lazy_value in sequence.py__iter__(): for lazy_value in sequence.py__iter__():
@@ -744,7 +738,7 @@ def _os_path_join(args_set, callback):
break break
if not is_first: if not is_first:
string += os.path.sep string += os.path.sep
string += force_unicode(s) string += s
is_first = False is_first = False
else: else:
return ValueSet([compiled.create_simple_object(sequence.inference_state, string)]) return ValueSet([compiled.create_simple_object(sequence.inference_state, string)])
-1
View File
@@ -2,7 +2,6 @@
Utilities for end-users. Utilities for end-users.
""" """
from __future__ import absolute_import
import __main__ import __main__
from collections import namedtuple from collections import namedtuple
import logging import logging
+3 -3
View File
@@ -18,7 +18,6 @@ from docopt import docopt
from jedi.parser.python import load_grammar from jedi.parser.python import load_grammar
from jedi.parser.diff import DiffParser from jedi.parser.diff import DiffParser
from jedi.parser.python import ParserWithRecovery from jedi.parser.python import ParserWithRecovery
from jedi._compatibility import u
from jedi.common import splitlines from jedi.common import splitlines
import jedi import jedi
@@ -37,14 +36,15 @@ def main(args):
with open(args['<file>']) as f: with open(args['<file>']) as f:
code = f.read() code = f.read()
grammar = load_grammar() grammar = load_grammar()
parser = ParserWithRecovery(grammar, u(code)) parser = ParserWithRecovery(grammar, code)
# Make sure used_names is loaded # Make sure used_names is loaded
parser.module.used_names parser.module.used_names
code = code + '\na\n' # Add something so the diff parser needs to run. code = code + '\na\n' # Add something so the diff parser needs to run.
lines = splitlines(code, keepends=True) lines = splitlines(code, keepends=True)
cProfile.runctx('run(parser, lines)', globals(), locals(), sort=args['-s']) cProfile.runctx('run(parser, lines)', globals(), locals(), sort=args['-s'])
if __name__ == '__main__': if __name__ == '__main__':
args = docopt(__doc__) args = docopt(__doc__)
main(args) main(args)
+1 -6
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python3.6 #!/usr/bin/env python3.6
# -*- coding: utf-8 -*-
""" """
Profile a piece of Python code with ``profile``. Tries a completion on a Profile a piece of Python code with ``profile``. Tries a completion on a
certain piece of code. certain piece of code.
@@ -19,11 +18,7 @@ Options:
""" """
import time import time
try: import profile
# For Python 2
import cProfile as profile
except ImportError:
import profile
import pstats import pstats
from docopt import docopt from docopt import docopt
+2 -6
View File
@@ -33,12 +33,11 @@ setup(name='jedi',
keywords='python completion refactoring vim', keywords='python completion refactoring vim',
long_description=readme, long_description=readme,
packages=find_packages(exclude=['test', 'test.*']), packages=find_packages(exclude=['test', 'test.*']),
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', python_requires='>=3.6',
install_requires=install_requires, install_requires=install_requires,
extras_require={ extras_require={
'testing': [ 'testing': [
# Pytest 5 doesn't support Python 2 anymore. 'pytest<6.0.0',
'pytest>=3.9.0,<5.0.0',
# docopt for sith doctests # docopt for sith doctests
'docopt', 'docopt',
# coloroma for colored debug output # coloroma for colored debug output
@@ -58,10 +57,7 @@ setup(name='jedi',
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'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.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.8',
-1
View File
@@ -44,7 +44,6 @@ Options:
--pudb Launch pudb when error is raised. --pudb Launch pudb when error is raised.
""" """
from __future__ import print_function, division, unicode_literals
from docopt import docopt from docopt import docopt
import json import json
-4
View File
@@ -272,9 +272,6 @@ dic = {str(key): ''}
#? str() #? str()
dic[''] dic['']
# Just skip Python 2 tests from here. EoL soon, I'm too lazy for it.
# python > 2.7
for x in {1: 3.0, '': 1j}: for x in {1: 3.0, '': 1j}:
#? int() str() #? int() str()
@@ -473,7 +470,6 @@ def test_func():
#? int() #? int()
tuple({1})[0] tuple({1})[0]
# python > 2.7
# ----------------- # -----------------
# PEP 3132 Extended Iterable Unpacking (star unpacking) # PEP 3132 Extended Iterable Unpacking (star unpacking)
# ----------------- # -----------------
-2
View File
@@ -5,8 +5,6 @@ Currently we're not supporting completion of them, but they should at least not
raise errors or return extremely strange results. raise errors or return extremely strange results.
""" """
# python >= 3.5
async def x(): async def x():
return 1 return 1
-10
View File
@@ -291,14 +291,6 @@ except ImportError as i_a:
i_a i_a
#? ImportError() #? ImportError()
i_a i_a
try:
import math
except ImportError, i_b:
# TODO check this only in Python2
##? ['i_b']
i_b
##? ImportError()
i_b
class MyException(Exception): class MyException(Exception):
@@ -344,8 +336,6 @@ def foo(my_t=some_defa
#? ['some_default'] #? ['some_default']
def foo(my_t=some_defa, my_t2=some_defa def foo(my_t=some_defa, my_t2=some_defa
# python > 2.7
#? ['my_type'] #? ['my_type']
def foo(my_t: lala=some_defa, my_t2: my_typ def foo(my_t: lala=some_defa, my_t2: my_typ
#? ['my_type'] #? ['my_type']
-1
View File
@@ -382,7 +382,6 @@ getattr(getattr, 1)
getattr(str, []) getattr(str, [])
# python >= 3.5
class Base(): class Base():
def ret(self, b): def ret(self, b):
return b return b
-2
View File
@@ -174,8 +174,6 @@ class X():
#? int() #? int()
X([1]).foo() X([1]).foo()
# set/dict comprehensions were introduced in 2.7, therefore:
# python >= 2.7
# ----------------- # -----------------
# dict comprehensions # dict comprehensions
# ----------------- # -----------------
-1
View File
@@ -388,7 +388,6 @@ k = 'a'
#? int() #? int()
some_dct[k] some_dct[k]
# python > 3.5
some_other_dct = dict(some_dct, c=set) some_other_dct = dict(some_dct, c=set)
#? int() #? int()
some_other_dct['a'] some_other_dct['a']
-2
View File
@@ -242,8 +242,6 @@ def x():
# yield from # yield from
# ----------------- # -----------------
# python > 2.7
def yield_from(): def yield_from():
yield from iter([1]) yield from iter([1])
-1
View File
@@ -97,7 +97,6 @@ def x(): pass
# ----------------- # -----------------
# Only keyword arguments are valid # Only keyword arguments are valid
# ----------------- # -----------------
# python >= 3.5
def x(bam, *, bar, baz): def x(bam, *, bar, baz):
pass pass
-2
View File
@@ -1,7 +1,5 @@
""" Pep-0484 type hinting """ """ Pep-0484 type hinting """
# python > 2.7
class A(): class A():
pass pass
@@ -1,4 +1,3 @@
# python >= 3.4
import typing import typing
from typing import ( from typing import (
Callable, Callable,
@@ -1,4 +1,3 @@
# python >= 3.4
from typing import ( from typing import (
Callable, Callable,
Dict, Dict,
@@ -1,4 +1,3 @@
# python >= 3.4
from typing import ( from typing import (
Any, Any,
Callable, Callable,
+1 -6
View File
@@ -1,7 +1,5 @@
""" """
Test the typing library, with docstrings. This is needed since annotations Test the typing library, with docstrings and annotations
are not supported in python 2.7 else then annotating by comment (and this is
still TODO at 2016-01-23)
""" """
import typing import typing
class B: class B:
@@ -295,8 +293,6 @@ y = type(PlainInt)
#? type.mro #? type.mro
y.mro y.mro
# python > 2.7
class TestDefaultDict(typing.DefaultDict[str, int]): class TestDefaultDict(typing.DefaultDict[str, int]):
def setdud(self): def setdud(self):
pass pass
@@ -323,7 +319,6 @@ for key in x.keys():
for value in x.values(): for value in x.values():
#? int() #? int()
value value
# python > 2.7
""" """
-2
View File
@@ -178,8 +178,6 @@ from datetime import datetime, timedelta
# magic methods # magic methods
# ----------------- # -----------------
# python >= 3.5
class C: class C:
def __sub__(self, other) -> int: ... def __sub__(self, other) -> int: ...
def __radd__(self, other) -> float: ... def __radd__(self, other) -> float: ...
-3
View File
@@ -1,4 +1,3 @@
# python > 2.7
import pytest import pytest
from pytest import fixture from pytest import fixture
@@ -130,8 +129,6 @@ def test_p(monkeypatch):
#? ['setattr'] #? ['setattr']
monkeypatch.setatt monkeypatch.setatt
# python > 2.7
#? ['capsysbinary'] #? ['capsysbinary']
def test_p(capsysbin def test_p(capsysbin
+41 -50
View File
@@ -133,48 +133,6 @@ weakref.ref(1)
#? int() None #? int() None
weakref.ref(1)() weakref.ref(1)()
# -----------------
# functools
# -----------------
import functools
basetwo = functools.partial(int, base=2)
#? int()
basetwo()
def function(a, b):
return a, b
a = functools.partial(function, 0)
#? int()
a('')[0]
#? str()
a('')[1]
kw = functools.partial(function, b=1.0)
tup = kw(1)
#? int()
tup[0]
#? float()
tup[1]
def my_decorator(f):
@functools.wraps(f)
def wrapper(*args, **kwds):
return f(*args, **kwds)
return wrapper
@my_decorator
def example(a):
return a
#? str()
example('')
# From GH #1574
#? float()
functools.wraps(functools.partial(str, 1))(lambda: 1.0)()
# ----------------- # -----------------
# sqlite3 (#84) # sqlite3 (#84)
# ----------------- # -----------------
@@ -253,7 +211,7 @@ z.read('name').upper
# ----------------- # -----------------
# contextlib # contextlib
# ----------------- # -----------------
# python > 2.7
from typing import Iterator from typing import Iterator
import contextlib import contextlib
with contextlib.closing('asd') as string: with contextlib.closing('asd') as string:
@@ -384,7 +342,6 @@ class Test(metaclass=Meta):
# Enum # Enum
# ----------------- # -----------------
# python > 2.7
import enum import enum
class X(enum.Enum): class X(enum.Enum):
@@ -409,8 +366,47 @@ X().name
X().attr_x.attr_y.value X().attr_x.attr_y.value
# ----------------- # -----------------
# functools Python 3.5+ # functools
# ----------------- # -----------------
import functools
basetwo = functools.partial(int, base=2)
#? int()
basetwo()
def function(a, b):
return a, b
a = functools.partial(function, 0)
#? int()
a('')[0]
#? str()
a('')[1]
kw = functools.partial(function, b=1.0)
tup = kw(1)
#? int()
tup[0]
#? float()
tup[1]
def my_decorator(f):
@functools.wraps(f)
def wrapper(*args, **kwds):
return f(*args, **kwds)
return wrapper
@my_decorator
def example(a):
return a
#? str()
example('')
# From GH #1574
#? float()
functools.wraps(functools.partial(str, 1))(lambda: 1.0)()
class X: class X:
def function(self, a, b): def function(self, a, b):
return a, b return a, b
@@ -463,11 +459,6 @@ X().just_partial('')[0]
#? str() #? str()
X().just_partial('')[1] X().just_partial('')[1]
# -----------------
# functools Python 3.8
# -----------------
# python >= 3.8 # python >= 3.8
@functools.lru_cache @functools.lru_cache
+1 -1
View File
@@ -1,4 +1,3 @@
# python > 2.7
from stub_folder import with_stub, stub_only, with_stub_folder, stub_only_folder from stub_folder import with_stub, stub_only, with_stub_folder, stub_only_folder
# ------------------------- # -------------------------
@@ -35,6 +34,7 @@ from stub_folder.with_stub import in_
#? ['with_stub', 'stub_only', 'with_stub_folder', 'stub_only_folder'] #? ['with_stub', 'stub_only', 'with_stub_folder', 'stub_only_folder']
from stub_folder. from stub_folder.
# ------------------------- # -------------------------
# Folders # Folders
# ------------------------- # -------------------------
-1
View File
@@ -135,7 +135,6 @@ set_t2.c
# ----------------- # -----------------
# pep 448 unpacking generalizations # pep 448 unpacking generalizations
# ----------------- # -----------------
# python >= 3.5
d = {'a': 3} d = {'a': 3}
dc = {v: 3 for v in ['a']} dc = {v: 3 for v in ['a']}
-1
View File
@@ -354,7 +354,6 @@ class DefinitelyNotGlobal:
# stubs # stubs
# ----------------- # -----------------
# python > 2.7
from stub_folder import with_stub from stub_folder import with_stub
#< ('stub:stub_folder.with_stub', 5, 4), ('stub_folder.with_stub', 5, 4), (0, 10) #< ('stub:stub_folder.with_stub', 5, 4), ('stub_folder.with_stub', 5, 4), (0, 10)
with_stub.stub_function with_stub.stub_function
+1 -3
View File
@@ -102,9 +102,7 @@ def collect_static_analysis_tests(base_dir, test_files):
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def venv_path(tmpdir_factory, environment): def venv_path(tmpdir_factory, environment):
if environment.version_info.major < 3: if isinstance(environment, InterpreterEnvironment):
pytest.skip("python -m venv does not exist in Python 2")
elif isinstance(environment, InterpreterEnvironment):
# The environment can be a tox virtualenv environment which we don't # The environment can be a tox virtualenv environment which we don't
# want, so use the system environment. # want, so use the system environment.
environment = get_system_environment( environment = get_system_environment(
@@ -1,14 +0,0 @@
"""
This is a module that imports the *standard library* unittest,
despite there being a local "unittest" module. It specifies that it
wants the stdlib one with the ``absolute_import`` __future__ import.
The twisted equivalent of this module is ``twisted.trial._synctest``.
"""
from __future__ import absolute_import
import unittest
class Assertions(unittest.TestCase):
pass
-14
View File
@@ -1,14 +0,0 @@
"""
This is a module that shadows a builtin (intentionally).
It imports a local module, which in turn imports stdlib unittest (the
name shadowed by this module). If that is properly resolved, there's
no problem. However, if jedi doesn't understand absolute_imports, it
will get this module again, causing infinite recursion.
"""
from local_module import Assertions
class TestCase(Assertions):
def test(self):
self.assertT
+8 -15
View File
@@ -3,25 +3,18 @@ A helper module for testing, improves compatibility for testing (as
``jedi._compatibility``) as well as introducing helper functions. ``jedi._compatibility``) as well as introducing helper functions.
""" """
import sys
from contextlib import contextmanager from contextlib import contextmanager
if sys.hexversion < 0x02070000:
import unittest2 as unittest
else:
import unittest
TestCase = unittest.TestCase
import os import os
import pytest import pytest
from os.path import abspath, dirname, join
from functools import partial, wraps from functools import partial, wraps
from jedi import Project from jedi import Project
from pathlib import Path
test_dir = dirname(abspath(__file__)) test_dir = Path(__file__).absolute().parent
test_dir_project = Project(test_dir) test_dir_project = Project(test_dir)
root_dir = dirname(test_dir) root_dir = test_dir.parent
example_dir = join(test_dir, 'examples') example_dir = test_dir.joinpath('examples')
sample_int = 1 # This is used in completion/imports.py sample_int = 1 # This is used in completion/imports.py
@@ -32,7 +25,7 @@ skip_if_not_windows = partial(pytest.param,
def get_example_dir(*names): def get_example_dir(*names):
return join(example_dir, *names) return example_dir.joinpath(*names)
def cwd_at(path): def cwd_at(path):
@@ -53,10 +46,10 @@ def cwd_at(path):
@contextmanager @contextmanager
def set_cwd(path, absolute_path=False): def set_cwd(path, absolute_path=False):
repo_root = os.path.dirname(test_dir) repo_root = test_dir.parent
oldcwd = os.getcwd() oldcwd = Path.cwd()
os.chdir(os.path.join(repo_root, path)) os.chdir(repo_root.joinpath(path))
try: try:
yield yield
finally: finally:

Some files were not shown because too many files have changed in this diff Show More