1
0
forked from VimPlug/jedi

Compare commits

..

1 Commits
v0.18.1 ... tmp

Author SHA1 Message Date
Dave Halter
354dab9503 Debug 2021-01-02 13:48:54 +01:00
64 changed files with 242 additions and 632 deletions

View File

@@ -1,5 +1,5 @@
name: ci
on: [push, pull_request]
on: push
jobs:
tests:
@@ -7,8 +7,8 @@ jobs:
strategy:
matrix:
os: [ubuntu-20.04, windows-2019]
python-version: ["3.10", "3.9", "3.8", "3.7", "3.6"]
environment: ['3.8', '3.10', '3.9', '3.7', '3.6', 'interpreter']
python-version: [3.9, 3.8, 3.7, 3.6]
environment: ['3.8', '3.9', '3.7', '3.6', 'interpreter']
steps:
- name: Checkout code
uses: actions/checkout@v2
@@ -27,6 +27,9 @@ jobs:
- name: Install dependencies
run: 'pip install .[testing]'
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
- name: Run tests
run: python -m pytest
env:

View File

@@ -61,7 +61,6 @@ Code Contributors
- Vladislav Serebrennikov (@endilll)
- Andrii Kolomoiets (@muffinmad)
- Leo Ryu (@Leo-Ryu)
- Joseph Birkner (@josephbirkner)
And a few more "anonymous" contributors.

View File

@@ -6,12 +6,7 @@ Changelog
Unreleased
++++++++++
0.18.1 (2021-11-17)
+++++++++++++++++++
- Implict namespaces are now a separate types in ``Name().type``
- Python 3.10 support
- Mostly bugfixes
0.18.0 (2020-12-25)
+++++++++++++++++++

View File

@@ -57,7 +57,7 @@ Supported Python Features
Limitations
-----------
In general Jedi's limit is quite high, but for very big projects or very
In general Jedi's limit are quite high, but for very big projects or very
complex code, sometimes Jedi intentionally stops type inference, to avoid
hanging for a long time.

View File

@@ -27,7 +27,7 @@ ad
load
"""
__version__ = '0.18.1'
__version__ = '0.18.0'
from jedi.api import Script, Interpreter, set_debug_function, preload_module
from jedi import settings

View File

@@ -7,6 +7,22 @@ import sys
import pickle
def cast_path(string):
"""
Take a bytes or str path and cast it to unicode.
Apparently it is perfectly fine to pass both byte and unicode objects into
the sys.path. This probably means that byte paths are normal at other
places as well.
Since this just really complicates everything and Python 2.7 will be EOL
soon anyway, just go with always strings.
"""
if isinstance(string, bytes):
return str(string, encoding='UTF-8', errors='replace')
return str(string)
def pickle_load(file):
try:
return pickle.load(file)

View File

@@ -13,6 +13,7 @@ from pathlib import Path
import parso
from parso.python import tree
from jedi._compatibility import cast_path
from jedi.parser_utils import get_executable_nodes
from jedi import debug
from jedi import settings
@@ -99,15 +100,13 @@ class Script:
"""
def __init__(self, code=None, *, path=None, environment=None, project=None):
self._orig_path = path
# An empty path (also empty string) should always result in no path.
if isinstance(path, str):
path = Path(path)
self.path = path.absolute() if path else None
if code is None:
if path is None:
raise ValueError("Must provide at least one of code or path")
# TODO add a better warning than the traceback!
with open(path, 'rb') as f:
code = f.read()
@@ -153,7 +152,7 @@ class Script:
if self.path is None:
file_io = None
else:
file_io = KnownContentFileIO(self.path, self._code)
file_io = KnownContentFileIO(cast_path(self.path), self._code)
if self.path is not None and self.path.suffix == '.pyi':
# We are in a stub file. Try to load the stub properly.
stub_module = load_proper_stub_module(
@@ -710,7 +709,7 @@ class Interpreter(Script):
"""
_allow_descriptor_getattr_default = True
def __init__(self, code, namespaces, *, project=None, **kwds):
def __init__(self, code, namespaces, **kwds):
try:
namespaces = [dict(n) for n in namespaces]
except Exception:
@@ -723,23 +722,16 @@ class Interpreter(Script):
if not isinstance(environment, InterpreterEnvironment):
raise TypeError("The environment needs to be an InterpreterEnvironment subclass.")
if project is None:
project = Project(Path.cwd())
super().__init__(code, environment=environment, project=project, **kwds)
super().__init__(code, environment=environment,
project=Project(Path.cwd()), **kwds)
self.namespaces = namespaces
self._inference_state.allow_descriptor_getattr = self._allow_descriptor_getattr_default
@cache.memoize_method
def _get_module_context(self):
if self.path is None:
file_io = None
else:
file_io = KnownContentFileIO(self.path, self._code)
tree_module_value = ModuleValue(
self._inference_state, self._module_node,
file_io=file_io,
file_io=KnownContentFileIO(str(self.path), self._code),
string_names=('__main__',),
code_lines=self._code_lines,
)

View File

@@ -27,7 +27,7 @@ from jedi.inference.compiled.mixed import MixedName
from jedi.inference.names import ImportName, SubModuleName
from jedi.inference.gradual.stub_value import StubModuleValue
from jedi.inference.gradual.conversion import convert_names, convert_values
from jedi.inference.base_value import ValueSet, HasNoContext
from jedi.inference.base_value import ValueSet
from jedi.api.keywords import KeywordName
from jedi.api import completion_cache
from jedi.api.helpers import filter_follow_imports
@@ -37,17 +37,13 @@ def _sort_names_by_start_pos(names):
return sorted(names, key=lambda s: s.start_pos or (0, 0))
def defined_names(inference_state, value):
def defined_names(inference_state, context):
"""
List sub-definitions (e.g., methods in class).
:type scope: Scope
:rtype: list of Name
"""
try:
context = value.as_context()
except HasNoContext:
return []
filter = next(context.get_filters())
names = [name for name in filter.values()]
return [Name(inference_state, n) for n in _sort_names_by_start_pos(names)]
@@ -763,7 +759,7 @@ class Name(BaseName):
"""
defs = self._name.infer()
return sorted(
unite(defined_names(self._inference_state, d) for d in defs),
unite(defined_names(self._inference_state, d.as_context()) for d in defs),
key=lambda s: s._name.start_pos or (0, 0)
)

View File

@@ -18,8 +18,7 @@ from jedi.inference import imports
from jedi.inference.base_value import ValueSet
from jedi.inference.helpers import infer_call_of_leaf, parse_dotted_names
from jedi.inference.context import get_global_filters
from jedi.inference.value import TreeInstance
from jedi.inference.docstring_utils import DocstringModule
from jedi.inference.value import TreeInstance, ModuleValue
from jedi.inference.names import ParamNameWrapper, SubModuleName
from jedi.inference.gradual.conversion import convert_values, convert_names
from jedi.parser_utils import cut_value_at_position
@@ -195,6 +194,7 @@ class Completion:
- In args: */**: no completion
- In params (also lambda): no completion before =
"""
grammar = self._inference_state.grammar
self.stack = stack = None
self._position = (
@@ -277,10 +277,6 @@ class Completion:
)
elif nonterminals[-1] in ('trailer', 'dotted_name') and nodes[-1] == '.':
dot = self._module_node.get_leaf_for_position(self._position)
if dot.type == "endmarker":
# This is a bit of a weird edge case, maybe we can somehow
# generalize this.
dot = leaf.get_previous_leaf()
cached_name, n = self._complete_trailer(dot.get_previous_leaf())
completion_names += n
elif self._is_parameter_completion():
@@ -466,12 +462,12 @@ class Completion:
def _complete_code_lines(self, code_lines):
module_node = self._inference_state.grammar.parse(''.join(code_lines))
module_value = DocstringModule(
in_module_context=self._module_context,
inference_state=self._inference_state,
module_node=module_node,
module_value = ModuleValue(
self._inference_state,
module_node,
code_lines=code_lines,
)
module_value.parent_context = self._module_context
return Completion(
self._inference_state,
module_value.as_context(),

View File

@@ -17,7 +17,7 @@ import parso
_VersionInfo = namedtuple('VersionInfo', 'major minor micro')
_SUPPORTED_PYTHONS = ['3.10', '3.9', '3.8', '3.7', '3.6']
_SUPPORTED_PYTHONS = ['3.9', '3.8', '3.7', '3.6']
_SAFE_PATHS = ['/usr/bin', '/usr/local/bin']
_CONDA_VAR = 'CONDA_PREFIX'
_CURRENT_VERSION = '%s.%s' % (sys.version_info.major, sys.version_info.minor)

View File

@@ -205,6 +205,7 @@ def filter_follow_imports(names, follow_builtin_imports=False):
class CallDetails:
def __init__(self, bracket_leaf, children, position):
['bracket_leaf', 'call_index', 'keyword_name_str']
self.bracket_leaf = bracket_leaf
self._children = children
self._position = position

View File

@@ -106,16 +106,7 @@ class Project:
with open(self._get_json_path(self._path), 'w') as f:
return json.dump((_SERIALIZER_VERSION, data), f)
def __init__(
self,
path,
*,
environment_path=None,
load_unsafe_extensions=False,
sys_path=None,
added_sys_path=(),
smart_sys_path=True,
) -> None:
def __init__(self, path, **kwargs):
"""
:param path: The base path for this project.
:param environment_path: The Python executable path, typically the path
@@ -134,22 +125,25 @@ class Project:
local directories. Otherwise you will have to rely on your packages
being properly configured on the ``sys.path``.
"""
def py2_comp(path, environment_path=None, load_unsafe_extensions=False,
sys_path=None, added_sys_path=(), smart_sys_path=True):
if isinstance(path, str):
path = Path(path).absolute()
self._path = path
if isinstance(path, str):
path = Path(path).absolute()
self._path = path
self._environment_path = environment_path
if sys_path is not None:
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._smart_sys_path = smart_sys_path
self._load_unsafe_extensions = load_unsafe_extensions
self._django = False
# Remap potential pathlib.Path entries
sys_path = list(map(str, sys_path))
self._sys_path = sys_path
self._smart_sys_path = smart_sys_path
self._load_unsafe_extensions = load_unsafe_extensions
self._django = False
# 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 """
self.added_sys_path = list(map(str, added_sys_path))
"""The sys path that is going to be added at the end of the """
py2_comp(path, **kwargs)
@property
def path(self):
@@ -334,8 +328,7 @@ class Project:
)
# 2. Search for identifiers in the project.
for module_context in search_in_file_ios(inference_state, file_ios,
name, complete=complete):
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 = [module_context.create_name(n) for n in names]
names = _remove_imports(names)

View File

@@ -106,7 +106,10 @@ def dbg(message, *args, color='GREEN'):
debug_function(color, i + 'dbg: ' + message % tuple(repr(a) for a in args))
def warning(message, *args, format=True):
def warning(message, *args, **kwargs):
format = kwargs.pop('format', True)
assert not kwargs
if debug_function and enable_warning:
i = ' ' * _debug_indent
if format:

View File

@@ -181,6 +181,8 @@ class InferenceState:
def parse_and_get_code(self, code=None, path=None,
use_latest_grammar=False, file_io=None, **kwargs):
if path is not None:
path = str(path)
if code is None:
if file_io is None:
file_io = FileIO(path)

View File

@@ -22,10 +22,6 @@ from jedi.cache import memoize_method
sentinel = object()
class HasNoContext(Exception):
pass
class HelperValueMixin:
def get_root_context(self):
value = self
@@ -265,7 +261,7 @@ class Value(HelperValueMixin):
return self.parent_context.is_stub()
def _as_context(self):
raise HasNoContext
raise NotImplementedError('Not all values need to be converted to contexts: %s', self)
@property
def name(self):

View File

@@ -8,8 +8,6 @@ import warnings
import re
import builtins
import typing
from pathlib import Path
from typing import Optional
from jedi.inference.compiled.getattr_static import getattr_static
@@ -181,9 +179,9 @@ class DirectObjectAccess:
def py__bool__(self):
return bool(self._obj)
def py__file__(self) -> Optional[Path]:
def py__file__(self):
try:
return Path(self._obj.__file__)
return self._obj.__file__
except AttributeError:
return None
@@ -213,22 +211,7 @@ class DirectObjectAccess:
def py__getitem__all_values(self):
if isinstance(self._obj, dict):
return [self._create_access_path(v) for v in self._obj.values()]
if isinstance(self._obj, (list, tuple)):
return [self._create_access_path(v) for v in self._obj]
if self.is_instance():
cls = DirectObjectAccess(self._inference_state, self._obj.__class__)
return cls.py__getitem__all_values()
try:
getitem = self._obj.__getitem__
except AttributeError:
pass
else:
annotation = DirectObjectAccess(self._inference_state, getitem).get_return_annotation()
if annotation is not None:
return [annotation]
return None
return self.py__iter__list()
def py__simple_getitem__(self, index):
if type(self._obj) not in ALLOWED_GETITEM_TYPES:
@@ -238,14 +221,8 @@ class DirectObjectAccess:
return self._create_access_path(self._obj[index])
def py__iter__list(self):
try:
iter_method = self._obj.__iter__
except AttributeError:
if not hasattr(self._obj, '__getitem__'):
return None
else:
p = DirectObjectAccess(self._inference_state, iter_method).get_return_annotation()
if p is not None:
return [p]
if type(self._obj) not in ALLOWED_GETITEM_TYPES:
# Get rid of side effects, we won't call custom `__getitem__`s.
@@ -329,9 +306,9 @@ class DirectObjectAccess:
except TypeError:
return False
def is_allowed_getattr(self, name, safe=True):
def is_allowed_getattr(self, name, unsafe=False):
# TODO this API is ugly.
if not safe:
if unsafe:
# Unsafe is mostly used to check for __getattr__/__getattribute__.
# getattr_static works for properties, but the underscore methods
# are just ignored (because it's safer and avoids more code
@@ -384,7 +361,7 @@ class DirectObjectAccess:
except AttributeError:
pass
else:
if module is not None and isinstance(module, str):
if module is not None:
try:
__import__(module)
# For some modules like _sqlite3, the __module__ for classes is

View File

@@ -187,7 +187,7 @@ def _find_syntax_node_name(inference_state, python_object):
try:
python_object = _get_object_to_check(python_object)
path = inspect.getsourcefile(python_object)
except (OSError, TypeError):
except TypeError:
# The type might not be known (e.g. class_with_dict.__weakref__)
return None
path = None if path is None else Path(path)

View File

@@ -7,7 +7,6 @@ goals:
2. Make it possible to handle different Python versions as well as virtualenvs.
"""
import collections
import os
import sys
import queue
@@ -169,7 +168,7 @@ class CompiledSubprocess:
def __init__(self, executable, env_vars=None):
self._executable = executable
self._env_vars = env_vars
self._inference_state_deletion_queue = collections.deque()
self._inference_state_deletion_queue = queue.deque()
self._cleanup_callable = lambda: None
def __repr__(self):

View File

@@ -4,10 +4,10 @@ import inspect
import importlib
import warnings
from pathlib import Path
from zipfile import ZipFile
from zipimport import zipimporter, ZipImportError
from zipimport import zipimporter
from importlib.machinery import all_suffixes
from jedi._compatibility import cast_path
from jedi.inference.compiled import access
from jedi import debug
from jedi import parser_utils
@@ -15,7 +15,7 @@ from jedi.file_io import KnownContentFileIO, ZipFileIO
def get_sys_path():
return sys.path
return list(map(cast_path, sys.path))
def load_module(inference_state, **kwargs):
@@ -93,22 +93,15 @@ def _iter_module_names(inference_state, paths):
# Python modules/packages
for path in paths:
try:
dir_entries = ((entry.name, entry.is_dir()) for entry in os.scandir(path))
dirs = os.scandir(path)
except OSError:
try:
zip_import_info = zipimporter(path)
# Unfortunately, there is no public way to access zipimporter's
# private _files member. We therefore have to use a
# custom function to iterate over the files.
dir_entries = _zip_list_subdirectory(
zip_import_info.archive, zip_import_info.prefix)
except ZipImportError:
# The file might not exist or reading it might lead to an error.
debug.warning("Not possible to list directory: %s", path)
continue
for name, is_dir in dir_entries:
# The file might not exist or reading it might lead to an error.
debug.warning("Not possible to list directory: %s", path)
continue
for dir_entry in dirs:
name = dir_entry.name
# First Namespaces then modules/stubs
if is_dir:
if dir_entry.is_dir():
# pycache is obviously not an interesting namespace. Also the
# name must be a valid identifier.
if name != '__pycache__' and name.isidentifier():
@@ -197,7 +190,7 @@ def _from_loader(loader, string):
except AttributeError:
return None, is_package
else:
module_path = get_filename(string)
module_path = cast_path(get_filename(string))
# To avoid unicode and read bytes, "overwrite" loader.get_source if
# possible.
@@ -219,7 +212,7 @@ def _from_loader(loader, string):
if code is None:
return None, is_package
if isinstance(loader, zipimporter):
return ZipFileIO(module_path, code, Path(loader.archive)), is_package
return ZipFileIO(module_path, code, Path(cast_path(loader.archive))), is_package
return KnownContentFileIO(module_path, code), is_package
@@ -237,17 +230,6 @@ def _get_source(loader, fullname):
name=fullname)
def _zip_list_subdirectory(zip_path, zip_subdir_path):
zip_file = ZipFile(zip_path)
zip_subdir_path = Path(zip_subdir_path)
zip_content_file_paths = zip_file.namelist()
for raw_file_name in zip_content_file_paths:
file_path = Path(raw_file_name)
if file_path.parent == zip_subdir_path:
file_path = file_path.relative_to(zip_subdir_path)
yield file_path.name, raw_file_name.endswith("/")
class ImplicitNSInfo:
"""Stores information returned from an implicit namespace spec"""
def __init__(self, name, paths):

View File

@@ -5,10 +5,10 @@ import re
from functools import partial
from inspect import Parameter
from pathlib import Path
from typing import Optional
from jedi import debug
from jedi.inference.utils import to_list
from jedi._compatibility import cast_path
from jedi.cache import memoize_method
from jedi.inference.filters import AbstractFilter
from jedi.inference.names import AbstractNameDefinition, ValueNameMixin, \
@@ -167,7 +167,7 @@ class CompiledValue(Value):
except AttributeError:
return super().py__simple_getitem__(index)
if access is None:
return super().py__simple_getitem__(index)
return NO_VALUES
return ValueSet([create_from_access_path(self.inference_state, access)])
@@ -293,7 +293,10 @@ class CompiledModule(CompiledValue):
return CompiledModuleContext(self)
def py__path__(self):
return self.access_handle.py__path__()
paths = self.access_handle.py__path__()
if paths is None:
return None
return map(cast_path, paths)
def is_package(self):
return self.py__path__() is not None
@@ -306,8 +309,11 @@ class CompiledModule(CompiledValue):
return ()
return tuple(name.split('.'))
def py__file__(self) -> Optional[Path]:
return self.access_handle.py__file__() # type: ignore[no-any-return]
def py__file__(self):
path = cast_path(self.access_handle.py__file__())
if path is None:
return None
return Path(path)
class CompiledName(AbstractNameDefinition):
@@ -434,7 +440,7 @@ class CompiledValueFilter(AbstractFilter):
access_handle = self.compiled_value.access_handle
return self._get(
name,
lambda name, safe: access_handle.is_allowed_getattr(name, safe=safe),
lambda name, unsafe: access_handle.is_allowed_getattr(name, unsafe),
lambda name: name in access_handle.dir(),
check_has_attribute=True
)
@@ -448,7 +454,7 @@ class CompiledValueFilter(AbstractFilter):
has_attribute, is_descriptor = allowed_getattr_callback(
name,
safe=not self._inference_state.allow_descriptor_getattr
unsafe=self._inference_state.allow_descriptor_getattr
)
if check_has_attribute and not has_attribute:
return []
@@ -472,7 +478,7 @@ class CompiledValueFilter(AbstractFilter):
from jedi.inference.compiled import builtin_from_name
names = []
needs_type_completions, dir_infos = self.compiled_value.access_handle.get_dir_infos()
# We could use `safe=False` here as well, especially as a parameter to
# We could use `unsafe` here as well, especially as a parameter to
# get_dir_infos. But this would lead to a lot of property executions
# that are probably not wanted. The drawback for this is that we
# have a different name for `get` and `values`. For `get` we always
@@ -480,7 +486,7 @@ class CompiledValueFilter(AbstractFilter):
for name in dir_infos:
names += self._get(
name,
lambda name, safe: dir_infos[name],
lambda name, unsafe: dir_infos[name],
lambda name: name in dir_infos,
)

View File

@@ -1,7 +1,5 @@
from abc import abstractmethod
from contextlib import contextmanager
from pathlib import Path
from typing import Optional
from parso.tree import search_ancestor
from parso.python.tree import Name
@@ -309,8 +307,8 @@ class FunctionContext(TreeContextMixin, ValueContext):
class ModuleContext(TreeContextMixin, ValueContext):
def py__file__(self) -> Optional[Path]:
return self._value.py__file__() # type: ignore[no-any-return]
def py__file__(self):
return self._value.py__file__()
def get_filters(self, until_position=None, origin_scope=None):
filters = self._value.get_filters(origin_scope)
@@ -327,7 +325,7 @@ class ModuleContext(TreeContextMixin, ValueContext):
yield from filters
def get_global_filter(self):
return GlobalNameFilter(self)
return GlobalNameFilter(self, self.tree_node)
@property
def string_names(self):
@@ -357,8 +355,8 @@ class NamespaceContext(TreeContextMixin, ValueContext):
def string_names(self):
return self._value.string_names
def py__file__(self) -> Optional[Path]:
return self._value.py__file__() # type: ignore[no-any-return]
def py__file__(self):
return self._value.py__file__()
class ClassContext(TreeContextMixin, ValueContext):
@@ -407,8 +405,8 @@ class CompiledModuleContext(CompiledContext):
def string_names(self):
return self._value.string_names
def py__file__(self) -> Optional[Path]:
return self._value.py__file__() # type: ignore[no-any-return]
def py__file__(self):
return self._value.py__file__()
def _get_global_filters_for_name(context, name_or_none, position):

View File

@@ -1,21 +0,0 @@
from jedi.inference.value import ModuleValue
from jedi.inference.context import ModuleContext
class DocstringModule(ModuleValue):
def __init__(self, in_module_context, **kwargs):
super().__init__(**kwargs)
self._in_module_context = in_module_context
def _as_context(self):
return DocstringModuleContext(self, self._in_module_context)
class DocstringModuleContext(ModuleContext):
def __init__(self, module_value, in_module_context):
super().__init__(module_value)
self._in_module_context = in_module_context
def get_filters(self, origin_scope=None, until_position=None):
yield from super().get_filters(until_position=until_position)
yield from self._in_module_context.get_filters()

View File

@@ -17,10 +17,12 @@ annotations.
import re
import warnings
from textwrap import dedent
from parso import parse, ParserSyntaxError
from jedi import debug
from jedi.common import indent_block
from jedi.inference.cache import inference_state_method_cache
from jedi.inference.base_value import iterator_to_value_set, ValueSet, \
NO_VALUES
@@ -180,40 +182,52 @@ def _strip_rst_role(type_str):
def _infer_for_statement_string(module_context, string):
code = dedent("""
def pseudo_docstring_stuff():
'''
Create a pseudo function for docstring statements.
Need this docstring so that if the below part is not valid Python this
is still a function.
'''
{}
""")
if string is None:
return []
potential_imports = re.findall(r'((?:\w+\.)*\w+)\.', string)
# Try to import module part in dotted name.
# (e.g., 'threading' in 'threading.Thread').
imports = "\n".join(f"import {p}" for p in potential_imports)
string = f'{imports}\n{string}'
for element in re.findall(r'((?:\w+\.)*\w+)\.', string):
# Try to import module part in dotted name.
# (e.g., 'threading' in 'threading.Thread').
string = 'import %s\n' % element + string
debug.dbg('Parse docstring code %s', string, color='BLUE')
grammar = module_context.inference_state.grammar
try:
module = grammar.parse(string, error_recovery=False)
module = grammar.parse(code.format(indent_block(string)), error_recovery=False)
except ParserSyntaxError:
return []
try:
# It's not the last item, because that's an end marker.
stmt = module.children[-2]
funcdef = next(module.iter_funcdefs())
# First pick suite, then simple_stmt and then the node,
# which is also not the last item, because there's a newline.
stmt = funcdef.children[-1].children[-1].children[-2]
except (AttributeError, IndexError):
return []
if stmt.type not in ('name', 'atom', 'atom_expr'):
return []
# Here we basically use a fake module that also uses the filters in
# the actual module.
from jedi.inference.docstring_utils import DocstringModule
m = DocstringModule(
in_module_context=module_context,
inference_state=module_context.inference_state,
module_node=module,
code_lines=[],
from jedi.inference.value import FunctionValue
function_value = FunctionValue(
module_context.inference_state,
module_context,
funcdef
)
return list(_execute_types_in_stmt(m.as_context(), stmt))
func_execution_context = function_value.as_context()
# Use the module of the param.
# TODO this module is not the module of the param in case of a function
# call. In that case it's the module of the function call.
# stuffed with content from a function call.
return list(_execute_types_in_stmt(func_execution_context, stmt))
def _execute_types_in_stmt(module_context, stmt):

View File

@@ -12,7 +12,7 @@ from parso.python.tree import Name, UsedNamesMapping
from jedi.inference import flow_analysis
from jedi.inference.base_value import ValueSet, ValueWrapper, \
LazyValueWrapper
from jedi.parser_utils import get_cached_parent_scope, get_parso_cache_node
from jedi.parser_utils import get_cached_parent_scope
from jedi.inference.utils import to_list
from jedi.inference.names import TreeNameDefinition, ParamName, \
AnonymousParamName, AbstractNameDefinition, NameWrapper
@@ -54,15 +54,11 @@ class FilterWrapper:
return self.wrap_names(self._wrapped_filter.values())
def _get_definition_names(parso_cache_node, used_names, name_key):
if parso_cache_node is None:
names = used_names.get(name_key, ())
return tuple(name for name in names if name.is_definition(include_setitem=True))
def _get_definition_names(used_names, name_key):
try:
for_module = _definition_name_cache[parso_cache_node]
for_module = _definition_name_cache[used_names]
except KeyError:
for_module = _definition_name_cache[parso_cache_node] = {}
for_module = _definition_name_cache[used_names] = {}
try:
return for_module[name_key]
@@ -74,40 +70,18 @@ def _get_definition_names(parso_cache_node, used_names, name_key):
return result
class _AbstractUsedNamesFilter(AbstractFilter):
class AbstractUsedNamesFilter(AbstractFilter):
name_class = TreeNameDefinition
def __init__(self, parent_context, node_context=None):
if node_context is None:
node_context = parent_context
self._node_context = node_context
self._parser_scope = node_context.tree_node
module_context = node_context.get_root_context()
# It is quite hacky that we have to use that. This is for caching
# certain things with a WeakKeyDictionary. However, parso intentionally
# uses slots (to save memory) and therefore we end up with having to
# have a weak reference to the object that caches the tree.
#
# Previously we have tried to solve this by using a weak reference onto
# used_names. However that also does not work, because it has a
# reference from the module, which itself is referenced by any node
# through parents.
path = module_context.py__file__()
if path is None:
# If the path is None, there is no guarantee that parso caches it.
self._parso_cache_node = None
else:
self._parso_cache_node = get_parso_cache_node(
module_context.inference_state.latest_grammar
if module_context.is_stub() else module_context.inference_state.grammar,
path
)
self._used_names = module_context.tree_node.get_used_names()
def __init__(self, parent_context, parser_scope):
self._parser_scope = parser_scope
self._module_node = self._parser_scope.get_root_node()
self._used_names = self._module_node.get_used_names()
self.parent_context = parent_context
def get(self, name):
return self._convert_names(self._filter(
_get_definition_names(self._parso_cache_node, self._used_names, name),
_get_definition_names(self._used_names, name),
))
def _convert_names(self, names):
@@ -118,7 +92,7 @@ class _AbstractUsedNamesFilter(AbstractFilter):
name
for name_key in self._used_names
for name in self._filter(
_get_definition_names(self._parso_cache_node, self._used_names, name_key),
_get_definition_names(self._used_names, name_key),
)
)
@@ -126,7 +100,7 @@ class _AbstractUsedNamesFilter(AbstractFilter):
return '<%s: %s>' % (self.__class__.__name__, self.parent_context)
class ParserTreeFilter(_AbstractUsedNamesFilter):
class ParserTreeFilter(AbstractUsedNamesFilter):
def __init__(self, parent_context, node_context=None, until_position=None,
origin_scope=None):
"""
@@ -135,7 +109,10 @@ class ParserTreeFilter(_AbstractUsedNamesFilter):
value, but for some type inference it's important to have a local
value of the other classes.
"""
super().__init__(parent_context, node_context)
if node_context is None:
node_context = parent_context
super().__init__(parent_context, node_context.tree_node)
self._node_context = node_context
self._origin_scope = origin_scope
self._until_position = until_position
@@ -149,7 +126,7 @@ class ParserTreeFilter(_AbstractUsedNamesFilter):
if parent.type == 'trailer':
return False
base_node = parent if parent.type in ('classdef', 'funcdef') else name
return get_cached_parent_scope(self._parso_cache_node, base_node) == self._parser_scope
return get_cached_parent_scope(self._used_names, base_node) == self._parser_scope
def _check_flows(self, names):
for name in sorted(names, key=lambda name: name.start_pos, reverse=True):
@@ -205,7 +182,7 @@ class AnonymousFunctionExecutionFilter(_FunctionExecutionFilter):
return AnonymousParamName(self._function_value, name)
class GlobalNameFilter(_AbstractUsedNamesFilter):
class GlobalNameFilter(AbstractUsedNamesFilter):
def get(self, name):
try:
names = self._used_names[name]

View File

@@ -196,43 +196,13 @@ def py__annotations__(funcdef):
return dct
def resolve_forward_references(context, all_annotations):
def resolve(node):
if node is None or node.type != 'string':
return node
node = _get_forward_reference_node(
context,
context.inference_state.compiled_subprocess.safe_literal_eval(
node.value,
),
)
if node is None:
# There was a string, but it's not a valid annotation
return None
# The forward reference tree has an additional root node ('eval_input')
# that we don't want. Extract the node we do want, that is equivalent to
# the nodes returned by `py__annotations__` for a non-quoted node.
node = node.children[0]
return node
return {name: resolve(node) for name, node in all_annotations.items()}
@inference_state_method_cache()
def infer_return_types(function, arguments):
"""
Infers the type of a function's return value,
according to type annotations.
"""
context = function.get_default_param_context()
all_annotations = resolve_forward_references(
context,
py__annotations__(function.tree_node),
)
all_annotations = py__annotations__(function.tree_node)
annotation = all_annotations.get("return", None)
if annotation is None:
# If there is no Python 3-type annotation, look for an annotation
@@ -247,10 +217,11 @@ def infer_return_types(function, arguments):
return NO_VALUES
return _infer_annotation_string(
context,
function.get_default_param_context(),
match.group(1).strip()
).execute_annotation()
context = function.get_default_param_context()
unknown_type_vars = find_unknown_type_vars(context, annotation)
annotation_values = infer_annotation(context, annotation)
if not unknown_type_vars:

View File

@@ -7,6 +7,7 @@ from pathlib import Path
from jedi import settings
from jedi.file_io import FileIO
from jedi._compatibility import cast_path
from jedi.parser_utils import get_cached_code_lines
from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference.gradual.stub_value import TypingModuleWrapper, StubModuleValue
@@ -43,6 +44,7 @@ def _create_stub_map(directory_path_info):
return
for entry in listed:
entry = cast_path(entry)
path = os.path.join(directory_path_info.path, entry)
if os.path.isdir(path):
init = os.path.join(path, '__init__.pyi')
@@ -167,6 +169,7 @@ def _try_to_load_stub(inference_state, import_names, python_value_set,
if len(import_names) == 1:
# foo-stubs
for p in sys_path:
p = cast_path(p)
init = os.path.join(p, *import_names) + '-stubs' + os.path.sep + '__init__.pyi'
m = _try_to_load_stub_from_file(
inference_state,

View File

@@ -431,9 +431,6 @@ class NewType(Value):
from jedi.inference.compiled.value import CompiledValueName
return CompiledValueName(self, 'NewType')
def __repr__(self) -> str:
return '<NewType: %s>%s' % (self.tree_node, self._type_value_set)
class CastFunction(ValueWrapper):
@repack_with_argument_clinic('type, object, /')

View File

@@ -422,13 +422,20 @@ def import_module(inference_state, import_names, parent_module_value, sys_path):
# The module might not be a package.
return NO_VALUES
file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info(
string=import_names[-1],
path=paths,
full_name=module_name,
is_global_search=False,
)
if is_pkg is None:
for path in paths:
# At the moment we are only using one path. So this is
# not important to be correct.
if not isinstance(path, list):
path = [path]
file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info(
string=import_names[-1],
path=path,
full_name=module_name,
is_global_search=False,
)
if is_pkg is not None:
break
else:
return NO_VALUES
if isinstance(file_io_or_ns, ImplicitNSInfo):

View File

@@ -341,12 +341,6 @@ class TreeNameDefinition(AbstractTreeName):
def py__doc__(self):
api_type = self.api_type
if api_type in ('function', 'class', 'property'):
if self.parent_context.get_root_context().is_stub():
from jedi.inference.gradual.conversion import convert_names
names = convert_names([self], prefer_stub_to_compiled=False)
if self not in names:
return _merge_name_docs(names)
# Make sure the names are not TreeNameDefinitions anymore.
return clean_scope_docstring(self.tree_name.get_definition())
@@ -414,9 +408,6 @@ class ParamNameInterface(_ParamMixin):
return 2
return 0
def infer_default(self):
return NO_VALUES
class BaseTreeParamName(ParamNameInterface, AbstractTreeName):
annotation_node = None

View File

@@ -12,7 +12,7 @@ count the function calls.
Settings
~~~~~~~~~~
Recursion settings are important if you don't want extremely
Recursion settings are important if you don't want extremly
recursive python code to go absolutely crazy.
The default values are based on experiments while completing the |jedi| library

View File

@@ -282,13 +282,12 @@ def get_module_contexts_containing_name(inference_state, module_contexts, name,
limit_reduction=limit_reduction)
def search_in_file_ios(inference_state, file_io_iterator, name,
limit_reduction=1, complete=False):
def search_in_file_ios(inference_state, file_io_iterator, name, limit_reduction=1):
parse_limit = _PARSED_FILE_LIMIT / limit_reduction
open_limit = _OPENED_FILE_LIMIT / limit_reduction
file_io_count = 0
parsed_file_count = 0
regex = re.compile(r'\b' + re.escape(name) + (r'' if complete else r'\b'))
regex = re.compile(r'\b' + re.escape(name) + r'\b')
for file_io in file_io_iterator:
file_io_count += 1
m = _check_fs(inference_state, file_io, regex)

View File

@@ -12,8 +12,6 @@ The signature here for bar should be `bar(b, c)` instead of bar(*args).
"""
from inspect import Parameter
from parso import tree
from jedi.inference.utils import to_list
from jedi.inference.names import ParamNameWrapper
from jedi.inference.helpers import is_big_annoying_library
@@ -24,11 +22,7 @@ def _iter_nodes_for_param(param_name):
from jedi.inference.arguments import TreeArguments
execution_context = param_name.parent_context
# Walk up the parso tree to get the FunctionNode we want. We use the parso
# tree rather than going via the execution context so that we're agnostic of
# the specific scope we're evaluating within (i.e: module or function,
# etc.).
function_node = tree.search_ancestor(param_name.tree_name, 'funcdef', 'lambdef')
function_node = execution_context.tree_node
module_node = function_node.get_root_node()
start = function_node.children[-1].start_pos
end = function_node.children[-1].end_pos

View File

@@ -738,13 +738,6 @@ def tree_name_to_values(inference_state, context, tree_name):
types = infer_expr_stmt(context, node, tree_name)
elif typ == 'with_stmt':
value_managers = context.infer_node(node.get_test_node_from_name(tree_name))
if node.parent.type == 'async_stmt':
# In the case of `async with` statements, we need to
# first get the coroutine from the `__aenter__` method,
# then "unwrap" via the `__await__` method
enter_methods = value_managers.py__getattribute__('__aenter__')
coro = enter_methods.execute_with_values()
return coro.py__await__().py__stop_iteration_returns()
enter_methods = value_managers.py__getattribute__('__enter__')
return enter_methods.execute_with_values()
elif typ in ('import_from', 'import_name'):

View File

@@ -186,6 +186,7 @@ def _get_buildout_script_paths(search_path: Path):
directory that look like python files.
:param search_path: absolute path to the module.
:type search_path: str
"""
project_root = _get_parent_dir_with_file(search_path, 'buildout.cfg')
if not project_root:
@@ -204,7 +205,7 @@ def _get_buildout_script_paths(search_path: Path):
except (UnicodeDecodeError, IOError) as e:
# Probably a binary file; permission error or race cond. because
# file got deleted. Ignore it.
debug.warning(str(e))
debug.warning(e)
continue

View File

@@ -344,8 +344,7 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
GenericClass(c, TupleGenericManager(generics)) for c in async_classes
).execute_annotation()
else:
# If there are annotations, prefer them over anything else.
if self.is_generator() and not self.infer_annotations():
if self.is_generator():
return ValueSet([iterable.Generator(inference_state, self)])
else:
return self.get_return_values()

View File

@@ -342,8 +342,6 @@ class SequenceLiteralValue(Sequence):
else:
with reraise_getitem_errors(TypeError, KeyError, IndexError):
node = self.get_tree_entries()[index]
if node == ':' or node.type == 'subscript':
return NO_VALUES
return self._defining_context.infer_node(node)
def py__iter__(self, contextualized_node=None):
@@ -409,6 +407,16 @@ class SequenceLiteralValue(Sequence):
else:
return [array_node]
def exact_key_items(self):
"""
Returns a generator of tuples like dict.items(), where the key is
resolved (as a string) and the values are still lazy values.
"""
for key_node, value in self.get_tree_entries():
for key in self._defining_context.infer_node(key_node):
if is_string(key):
yield key.get_safe_value(), LazyTreeValue(self._defining_context, value)
def __repr__(self):
return "<%s of %s>" % (self.__class__.__name__, self.atom)
@@ -464,16 +472,6 @@ class DictLiteralValue(_DictMixin, SequenceLiteralValue, _DictKeyMixin):
return ValueSet([FakeList(self.inference_state, lazy_values)])
def exact_key_items(self):
"""
Returns a generator of tuples like dict.items(), where the key is
resolved (as a string) and the values are still lazy values.
"""
for key_node, value in self.get_tree_entries():
for key in self._defining_context.infer_node(key_node):
if is_string(key):
yield key.get_safe_value(), LazyTreeValue(self._defining_context, value)
def _dict_values(self):
return ValueSet.from_sets(
self._defining_context.infer_node(v)

View File

@@ -114,7 +114,7 @@ class ClassFilter(ParserTreeFilter):
while node is not None:
if node == self._parser_scope or node == self.parent_context:
return True
node = get_cached_parent_scope(self._parso_cache_node, node)
node = get_cached_parent_scope(self._used_names, node)
return False
def _access_possible(self, name):

View File

@@ -64,7 +64,7 @@ class ModuleMixin(SubModuleDictMixin):
parent_context=self.as_context(),
origin_scope=origin_scope
),
GlobalNameFilter(self.as_context()),
GlobalNameFilter(self.as_context(), self.tree_node),
)
yield DictFilter(self.sub_modules_dict())
yield DictFilter(self._module_attributes_dict())
@@ -148,7 +148,7 @@ class ModuleValue(ModuleMixin, TreeValue):
if file_io is None:
self._path: Optional[Path] = None
else:
self._path = file_io.path
self._path = Path(file_io.path)
self.string_names = string_names # Optional[Tuple[str, ...]]
self.code_lines = code_lines
self._is_package = is_package

View File

@@ -1,6 +1,3 @@
from pathlib import Path
from typing import Optional
from jedi.inference.cache import inference_state_method_cache
from jedi.inference.filters import DictFilter
from jedi.inference.names import ValueNameMixin, AbstractNameDefinition
@@ -44,7 +41,7 @@ class ImplicitNamespaceValue(Value, SubModuleDictMixin):
string_name = self.py__package__()[-1]
return ImplicitNSName(self, string_name)
def py__file__(self) -> Optional[Path]:
def py__file__(self):
return None
def py__package__(self):

View File

@@ -216,14 +216,11 @@ def is_scope(node):
def _get_parent_scope_cache(func):
cache = WeakKeyDictionary()
def wrapper(parso_cache_node, node, include_flows=False):
if parso_cache_node is None:
return func(node, include_flows)
def wrapper(used_names, node, include_flows=False):
try:
for_module = cache[parso_cache_node]
for_module = cache[used_names]
except KeyError:
for_module = cache[parso_cache_node] = {}
for_module = cache[used_names] = {}
try:
return for_module[node]
@@ -273,18 +270,7 @@ def get_cached_code_lines(grammar, path):
Basically access the cached code lines in parso. This is not the nicest way
to do this, but we avoid splitting all the lines again.
"""
return get_parso_cache_node(grammar, path).lines
def get_parso_cache_node(grammar, path):
"""
This is of course not public. But as long as I control parso, this
shouldn't be a problem. ~ Dave
The reason for this is mostly caching. This is obviously also a sign of a
broken caching architecture.
"""
return parser_cache[grammar._hashed][path]
return parser_cache[grammar._hashed][path].lines
def cut_value_at_position(leaf, position):

View File

@@ -31,15 +31,7 @@ def execute(callback):
def infer_anonymous_param(func):
def get_returns(value):
if value.tree_node.annotation is not None:
result = value.execute_with_values()
if any(v.name.get_qualified_names(include_module_names=True)
== ('typing', 'Generator')
for v in result):
return ValueSet.from_sets(
v.py__getattribute__('__next__').execute_annotation()
for v in result
)
return result
return value.execute_with_values()
# In pytest we need to differentiate between generators and normal
# returns.
@@ -51,9 +43,6 @@ def infer_anonymous_param(func):
return function_context.get_return_values()
def wrapper(param_name):
# parameters with an annotation do not need special handling
if param_name.annotation_node:
return func(param_name)
is_pytest_param, param_name_is_function_name = \
_is_a_pytest_param_and_inherited(param_name)
if is_pytest_param:
@@ -140,10 +129,6 @@ def _iter_pytest_modules(module_context, skip_own_module=False):
if file_io is not None:
folder = file_io.get_parent_folder()
sys_path = module_context.inference_state.get_sys_path()
# prevent an infinite loop when reaching the root of the current drive
last_folder = None
while any(folder.path.startswith(p) for p in sys_path):
file_io = folder.get_file_io('conftest.py')
if Path(file_io.path) != module_context.py__file__():
@@ -154,11 +139,6 @@ def _iter_pytest_modules(module_context, skip_own_module=False):
pass
folder = folder.get_parent_folder()
# prevent an infinite for loop if the same parent folder is return twice
if last_folder is not None and folder.path == last_folder.path:
break
last_folder = folder # keep track of the last found parent name
for names in _PYTEST_FIXTURE_MODULES:
for module_value in module_context.inference_state.import_module(names):
yield module_value.as_context()

View File

@@ -35,7 +35,7 @@ setup(name='jedi',
install_requires=['parso>=0.8.0,<0.9.0'],
extras_require={
'testing': [
'pytest<7.0.0',
'pytest<6.0.0',
# docopt for sith doctests
'docopt',
# coloroma for colored debug output
@@ -61,7 +61,6 @@ setup(name='jedi',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Text Editors :: Integrated Development Environments (IDE)',
'Topic :: Utilities',

View File

@@ -44,8 +44,6 @@ b[int():]
#? list()
b[:]
#? int()
b[:, :-1]
#? 3
b[:]
@@ -69,20 +67,6 @@ class _StrangeSlice():
#? slice()
_StrangeSlice()[1:2]
for x in b[:]:
#? int()
x
for x in b[:, :-1]:
#?
x
class Foo:
def __getitem__(self, item):
return item
#?
Foo()[:, :-1][0]
# -----------------
# iterable multiplication

View File

@@ -26,6 +26,11 @@ async def y():
x().__await__().__next
return 2
async def x2():
async with open('asdf') as f:
#? ['readlines']
f.readlines
class A():
@staticmethod
async def b(c=1, d=2):
@@ -100,22 +105,3 @@ async def f():
f = await C().async_for_classmethod()
#? C()
f
class AsyncCtxMgr:
def some_method():
pass
async def __aenter__(self):
return self
async def __aexit__(self, *args):
pass
async def asyncctxmgr():
async with AsyncCtxMgr() as acm:
#? AsyncCtxMgr()
acm
#? ['some_method']
acm.som

View File

@@ -284,13 +284,6 @@ def doctest_with_space():
import_issu
"""
def doctest_issue_github_1748():
"""From GitHub #1748
#? 10 []
This. Al
"""
pass
def docstring_rst_identifiers():
"""

View File

@@ -309,8 +309,3 @@ def annotation2() -> Iterator[float]:
next(annotation1())
#? float()
next(annotation2())
# annotations should override generator inference
#? float()
annotation1()

View File

@@ -1 +0,0 @@
mod1_name = 'mod1'

View File

@@ -1 +0,0 @@
mod2_name = 'mod2'

View File

@@ -1,18 +0,0 @@
import sys
import os
from os.path import dirname
sys.path.insert(0, os.path.join(dirname(__file__), 'namespace2'))
sys.path.insert(0, os.path.join(dirname(__file__), 'namespace1'))
#? ['mod1']
import pkg1.pkg2.mod1
#? ['mod2']
import pkg1.pkg2.mod2
#? ['mod1_name']
pkg1.pkg2.mod1.mod1_name
#? ['mod2_name']
pkg1.pkg2.mod2.mod2_name

View File

@@ -23,9 +23,11 @@ def builtin_test():
import sqlite3
# classes is a local module that has an __init__.py and can therefore not be
# found.
# found. test can be found.
#? []
import classes
#? ['test']
import test
#? ['timedelta']
from datetime import timedel

View File

@@ -58,16 +58,6 @@ def typed_bound_generic_passthrough(x: TList) -> TList:
return x
# Forward references are more likely with custom types, however this aims to
# test just the handling of the quoted type rather than any other part of the
# machinery.
def typed_quoted_return_generic_passthrough(x: T) -> 'List[T]':
return [x]
def typed_quoted_input_generic_passthrough(x: 'Tuple[T]') -> T:
x
return x[0]
for a in untyped_passthrough(untyped_list_str):
#? str()
@@ -156,23 +146,6 @@ for q in typed_bound_generic_passthrough(typed_list_str):
q
for r in typed_quoted_return_generic_passthrough("something"):
#? str()
r
for s in typed_quoted_return_generic_passthrough(42):
#? int()
s
#? str()
typed_quoted_input_generic_passthrough(("something",))
#? int()
typed_quoted_input_generic_passthrough((42,))
class CustomList(List):
def get_first(self):
return self[0]

View File

@@ -1,5 +1,3 @@
from typing import Generator
import pytest
from pytest import fixture
@@ -66,11 +64,6 @@ def lala(my_fixture):
def lala(my_fixture):
pass
# overriding types of a fixture should be possible
def test_x(my_yield_fixture: str):
#? str()
my_yield_fixture
# -----------------
# completion
# -----------------
@@ -171,15 +164,3 @@ def test_inheritance_fixture(inheritance_fixture, caplog):
@pytest.fixture
def caplog(caplog):
yield caplog
# -----------------
# Generator with annotation
# -----------------
@pytest.fixture
def with_annot() -> Generator[float, None, None]:
pass
def test_with_annot(inheritance_fixture, with_annot):
#? float()
with_annot

View File

@@ -1,6 +1,7 @@
import os
import sys
import subprocess
from itertools import count
import pytest
@@ -9,6 +10,9 @@ from . import run
from . import refactor
from jedi import InterpreterEnvironment, get_system_environment
from jedi.inference.compiled.value import create_from_access_path
from jedi.inference.imports import _load_python_module
from jedi.file_io import KnownContentFileIO
from jedi.inference.base_value import ValueSet
from jedi.api.interpreter import MixedModuleContext
# For interpreter tests sometimes the path of this directory is in the sys
@@ -159,6 +163,19 @@ def create_compiled_object(inference_state):
)
@pytest.fixture
def module_injector():
counter = count()
def module_injector(inference_state, names, code):
assert isinstance(names, tuple)
file_io = KnownContentFileIO('/foo/bar/module-injector-%s.py' % next(counter), code)
v = _load_python_module(inference_state, file_io, names)
inference_state.module_cache.add(names, ValueSet([v]))
return module_injector
@pytest.fixture(params=[False, True])
def class_findable(monkeypatch, request):
if not request.param:

View File

@@ -104,14 +104,10 @@ import os
import re
import sys
import operator
if sys.version_info < (3, 8):
literal_eval = eval
else:
from ast import literal_eval
from ast import literal_eval
from io import StringIO
from functools import reduce
from unittest.mock import ANY
from pathlib import Path
import parso
from _pytest.outcomes import Skipped
@@ -126,7 +122,6 @@ from jedi.api.environment import get_default_environment, get_system_environment
from jedi.inference.gradual.conversion import convert_values
from jedi.inference.analysis import Warning
test_dir = Path(__file__).absolute().parent
TEST_COMPLETIONS = 0
TEST_INFERENCE = 1
@@ -178,7 +173,6 @@ class IntegrationTestCase(BaseTestCase):
self.start = start
self.line = line
self.path = path
self._project = jedi.Project(test_dir)
@property
def module_name(self):
@@ -194,12 +188,7 @@ class IntegrationTestCase(BaseTestCase):
self.line_nr_test, self.line.rstrip())
def script(self, environment):
return jedi.Script(
self.source,
path=self.path,
environment=environment,
project=self._project
)
return jedi.Script(self.source, path=self.path, environment=environment)
def run(self, compare_cb, environment=None):
testers = {
@@ -274,7 +263,7 @@ class IntegrationTestCase(BaseTestCase):
self.correct = self.correct.strip()
compare = sorted(
(('stub:' if r.is_stub() else '')
+ re.sub(r'^completion\.', '', r.module_name),
+ re.sub(r'^test\.completion\.', '', r.module_name),
r.line,
r.column)
for r in result

View File

@@ -1,16 +1,11 @@
from os.path import join, sep as s, dirname, expanduser
import os
from textwrap import dedent
from itertools import count
from pathlib import Path
import pytest
from ..helpers import root_dir
from jedi.api.helpers import _start_match, _fuzzy_match
from jedi.inference.imports import _load_python_module
from jedi.file_io import KnownContentFileIO
from jedi.inference.base_value import ValueSet
def test_in_whitespace(Script):
@@ -405,22 +400,6 @@ def test_ellipsis_completion(Script):
assert Script('...').complete() == []
@pytest.fixture
def module_injector():
counter = count()
def module_injector(inference_state, names, code):
assert isinstance(names, tuple)
file_io = KnownContentFileIO(
Path('foo/bar/module-injector-%s.py' % next(counter)).absolute(),
code
)
v = _load_python_module(inference_state, file_io, names)
inference_state.module_cache.add(names, ValueSet([v]))
return module_injector
def test_completion_cache(Script, module_injector):
"""
For some modules like numpy, tensorflow or pandas we cache docstrings and
@@ -457,7 +436,3 @@ def test_module_completions(Script, module):
# Just make sure that there are no errors
c.type
c.docstring()
def test_whitespace_at_end_after_dot(Script):
assert 'strip' in [c.name for c in Script('str. ').complete()]

View File

@@ -37,17 +37,6 @@ def test_operator_doc(Script):
assert len(d.docstring()) > 100
@pytest.mark.parametrize(
'code, help_part', [
('str', 'Create a new string object'),
('str.strip', 'Return a copy of the string'),
]
)
def test_stdlib_doc(Script, code, help_part):
h, = Script(code).help()
assert help_part in h.docstring(raw=True)
def test_lambda(Script):
d, = Script('lambda x: x').help(column=0)
assert d.type == 'keyword'

View File

@@ -603,11 +603,8 @@ def test_dict_getitem(code, types):
@pytest.mark.parametrize(
'code, expected', [
('DunderCls()[0]', 'int'),
('dunder[0]', 'int'),
('next(DunderCls())', 'float'),
('next(dunder)', 'float'),
('for x in DunderCls(): x', 'str'),
#('for x in dunder: x', 'str'),
]
)
def test_dunders(class_is_findable, code, expected):
@@ -626,8 +623,6 @@ def test_dunders(class_is_findable, code, expected):
if not class_is_findable:
DunderCls.__name__ = 'asdf'
dunder = DunderCls()
n, = jedi.Interpreter(code, [locals()]).infer()
assert n.name == expected
@@ -711,31 +706,3 @@ def test_negate():
assert x.name == 'int'
value, = x._name.infer()
assert value.get_safe_value() == -3
def test_complete_not_findable_class_source():
class TestClass():
ta=1
ta1=2
# Simulate the environment where the class is defined in
# an interactive session and therefore inspect module
# cannot find its source code and raises OSError (Py 3.10+) or TypeError.
TestClass.__module__ = "__main__"
# There is a pytest __main__ module we have to remove temporarily.
module = sys.modules.pop("__main__")
try:
interpreter = jedi.Interpreter("TestClass.", [locals()])
completions = interpreter.complete(column=10, line=1)
finally:
sys.modules["__main__"] = module
assert "ta" in [c.name for c in completions]
assert "ta1" in [c.name for c in completions]
def test_param_infer_default():
abs_sig, = jedi.Interpreter('abs(', [{'abs': abs}]).get_signatures()
param, = abs_sig.params
assert param.name == 'x'
assert param.infer_default() == []

View File

@@ -189,9 +189,3 @@ def test_no_error(get_names):
def test_is_side_effect(get_names, code, index, is_side_effect):
names = get_names(code, references=True, all_scopes=True)
assert names[index].is_side_effect() == is_side_effect
def test_no_defined_names(get_names):
definition, = get_names("x = (1, 2)")
assert not definition.defined_names()

View File

@@ -68,10 +68,6 @@ def test_load_save_project(tmpdir):
dict(all_scopes=True)),
('some_search_test_var', ['test_api.test_project.test_search.some_search_test_var'],
dict(complete=True, all_scopes=True)),
# Make sure that the searched name is not part of the file, by
# splitting it up.
('some_search_test_v' + 'a', ['test_api.test_project.test_search.some_search_test_var'],
dict(complete=True, all_scopes=True)),
('sample_int', ['helpers.sample_int'], {}),
('sample_int', ['helpers.sample_int'], dict(all_scopes=True)),
@@ -150,7 +146,7 @@ def test_search(string, full_names, kwargs):
defs = project.complete_search(string, **kwargs)
else:
defs = project.search(string, **kwargs)
assert sorted([('stub:' if d.is_stub() else '') + (d.full_name or d.name) for d in defs]) == full_names
assert sorted([('stub:' if d.is_stub() else '') + d.full_name for d in defs]) == full_names
@pytest.mark.parametrize(

View File

@@ -64,6 +64,6 @@ def test_wrong_encoding(Script, tmpdir):
# Use both latin-1 and utf-8 (a really broken file).
x.write_binary('foobar = 1\nä'.encode('latin-1') + 'ä'.encode('utf-8'))
project = Project(tmpdir.strpath)
project = Project('.', sys_path=[tmpdir.strpath])
c, = Script('import x; x.foo', project=project).complete()
assert c.name == 'foobar'

View File

@@ -101,16 +101,6 @@ def test_correct_zip_package_behavior(Script, inference_state, environment, code
assert value.py__package__() == []
@pytest.mark.parametrize("code,names", [
("from pkg.", {"module", "nested", "namespace"}),
("from pkg.nested.", {"nested_module"})
])
def test_zip_package_import_complete(Script, environment, code, names):
sys_path = environment.get_sys_path() + [str(pkg_zip_path)]
completions = Script(code, project=Project('.', sys_path=sys_path)).complete()
assert names == {c.name for c in completions}
def test_find_module_not_package_zipped(Script, inference_state, environment):
path = get_example_dir('zipped_imports', 'not_pkg.zip')
sys_path = environment.get_sys_path() + [path]
@@ -334,13 +324,12 @@ def test_compiled_import_none(monkeypatch, Script):
# context that was initially given, but now we just work with the file
# system.
(os.path.join(THIS_DIR, 'test_docstring.py'), False,
('test_inference', 'test_imports')),
('test', 'test_inference', 'test_imports')),
(os.path.join(THIS_DIR, '__init__.py'), True,
('test_inference', 'test_imports')),
('test', 'test_inference', 'test_imports')),
]
)
def test_get_modules_containing_name(inference_state, path, goal, is_package):
inference_state.project = Project(test_dir)
module = imports._load_python_module(
inference_state,
FileIO(path),

View File

@@ -267,19 +267,19 @@ def test_pow_signature(Script, environment):
@pytest.mark.parametrize(
'code, signature', [
[dedent('''
# identifier:A
import functools
def f(x):
pass
def x(f):
@functools.wraps(f)
def wrapper(*args):
# Have no arguments here, but because of wraps, the signature
# should still be f's.
return f(*args)
return wrapper
x(f)('''), 'f(x, /)'],
[dedent('''
# identifier:B
import functools
def f(x):
pass
@@ -292,26 +292,6 @@ def test_pow_signature(Script, environment):
return wrapper
x(f)('''), 'f()'],
[dedent('''
# identifier:C
import functools
def f(x: int, y: float):
pass
@functools.wraps(f)
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
wrapper('''), 'f(x: int, y: float)'],
[dedent('''
# identifier:D
def f(x: int, y: float):
pass
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
wrapper('''), 'wrapper(x: int, y: float)'],
]
)
def test_wraps_signature(Script, code, signature):

View File

@@ -1,9 +1,5 @@
import gc
from pathlib import Path
from jedi import parser_utils
from parso import parse
from parso.cache import parser_cache
from parso.python import tree
import pytest
@@ -71,18 +67,3 @@ def test_get_signature(code, signature):
if node.type == 'simple_stmt':
node = node.children[0]
assert parser_utils.get_signature(node) == signature
def test_parser_cache_clear(Script):
"""
If parso clears its cache, Jedi should not keep those resources, they
should be freed.
"""
script = Script("a = abs\na", path=Path(__file__).parent / 'parser_cache_test_foo.py')
script.complete()
module_id = id(script._module_node)
del parser_cache[script._inference_state.grammar._hashed][script.path]
del script
gc.collect()
assert module_id not in [id(m) for m in gc.get_referrers(tree.Module)]

View File

@@ -85,7 +85,7 @@ class TestSetupReadline(unittest.TestCase):
}
# There are quite a few differences, because both Windows and Linux
# (posix and nt) librariesare included.
assert len(difference) < 30
assert len(difference) < 15
def test_local_import(self):
s = 'import test.test_utils'