Compare commits

..

1 Commits

Author SHA1 Message Date
David Brochart 76c1e03f07 Fix typo 2026-02-13 11:01:08 +00:00
75 changed files with 288 additions and 320 deletions
+6 -6
View File
@@ -7,8 +7,8 @@ jobs:
strategy:
matrix:
os: [ubuntu-24.04, windows-2022]
python-version: ["3.13", "3.12", "3.11", "3.10"]
environment: ['3.13', '3.12', '3.11', '3.10', 'interpreter']
python-version: ["3.13", "3.12", "3.11", "3.10", "3.9", "3.8"]
environment: ['3.8', '3.13', '3.12', '3.11', '3.10', '3.9', 'interpreter']
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -27,7 +27,7 @@ jobs:
allow-prereleases: true
- name: Install dependencies
run: 'pip install .[dev]'
run: 'pip install .[testing]'
- name: Run tests
run: python -m pytest
@@ -43,12 +43,12 @@ jobs:
submodules: recursive
- name: Install dependencies
run: 'pip install .[dev]'
run: 'pip install .[qa]'
- name: Run tests
run: |
python -m flake8 jedi test setup.py
zuban check
python -m mypy jedi sith.py setup.py
coverage:
runs-on: ubuntu-24.04
@@ -60,7 +60,7 @@ jobs:
submodules: recursive
- name: Install dependencies
run: 'pip install .[dev] coverage'
run: 'pip install .[testing] coverage'
- name: Run tests
run: |
+3 -3
View File
@@ -102,7 +102,7 @@ Features and Limitations
Jedi's features are listed here:
`Features <https://jedi.readthedocs.org/en/latest/docs/features.html>`_.
You can run Jedi on Python 3.10+ but it should also
You can run Jedi on Python 3.8+ but it should also
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>`_
very well.
@@ -183,10 +183,10 @@ The test suite uses ``pytest``::
pip install pytest
If you want to test only a specific Python version (e.g. Python 3.14), it is as
If you want to test only a specific Python version (e.g. Python 3.8), it is as
easy as::
python3.14 -m pytest
python3.8 -m pytest
For more detailed information visit the `testing documentation
<https://jedi.readthedocs.org/en/latest/docs/testing.html>`_.
+21 -7
View File
@@ -42,7 +42,7 @@ def pytest_addoption(parser):
help="Warnings are treated as errors.")
parser.addoption("--env", action='store',
help="Execute the tests in that environment (e.g. 314 for python3.14).")
help="Execute the tests in that environment (e.g. 39 for python3.9).")
parser.addoption("--interpreter-env", "-I", action='store_true',
help="Don't use subprocesses to guarantee having safe "
"code execution. Useful for debugging.")
@@ -133,13 +133,11 @@ def goto_or_help(request, Script):
@pytest.fixture(scope='session', params=['goto', 'help', 'infer'])
def goto_or_help_or_infer(request, Script):
class GotoOrHelpOrInfer:
def __call__(self, code, *args, **kwargs):
return getattr(Script(code), request.param)(*args, **kwargs)
def do(code, *args, **kwargs):
return getattr(Script(code), request.param)(*args, **kwargs)
type = request.param
return GotoOrHelpOrInfer()
do.type = request.param
return do
@pytest.fixture(scope='session', params=['goto', 'complete', 'help'])
@@ -164,3 +162,19 @@ def skip_pre_python311(environment):
# 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_python38(environment):
if environment.version_info < (3, 8):
# 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_python37(environment):
if environment.version_info < (3, 7):
# This if is just needed to avoid that tests ever skip way more than
# they should for all Python versions.
pytest.skip()
+1 -1
View File
@@ -107,7 +107,7 @@ Completions
>>> code = '''import json; json.l'''
>>> script = jedi.Script(code, path='example.py')
>>> script
<Script: 'example.py' <SameEnvironment: 3.14.0 in /usr>>
<Script: 'example.py' <SameEnvironment: 3.9.0 in /usr>>
>>> completions = script.complete(1, 19)
>>> completions
[<Completion: load>, <Completion: loads>]
+1 -1
View File
@@ -16,7 +16,7 @@ Jedi's main API calls and features are:
Basic Features
--------------
- Python 3.10+ support
- Python 3.8+ support
- Ignores syntax errors and wrong indentation
- Can deal with complex module / function / class structures
- Great ``virtualenv``/``venv`` support
+2 -2
View File
@@ -7,10 +7,10 @@ The test suite depends on ``pytest``::
pip install pytest
If you want to test only a specific Python version (e.g. Python 3.14), it is as
If you want to test only a specific Python version (e.g. Python 3.8), it is as
easy as::
python3.14 -m pytest
python3.8 -m pytest
Tests are also run automatically on `GitHub Actions
<https://github.com/davidhalter/jedi/actions>`_.
+2 -2
View File
@@ -3,7 +3,7 @@
Using Jedi
==========
|jedi| is can be used with a variety of :ref:`plugins <editor-plugins>`,
|jedi| can be used with a variety of :ref:`plugins <editor-plugins>`,
:ref:`language servers <language-servers>` and other software.
It is also possible to use |jedi| in the :ref:`Python shell or with IPython
<repl-completion>`.
@@ -97,7 +97,7 @@ Web Debugger
xonsh shell
~~~~~~~~~~~
Jedi is a preinstalled extension in `xonsh shell <https://xon.sh/contents.html>`_.
Jedi is a preinstalled extension in `xonsh shell <https://xon.sh/contents.html>`_.
Run the following command to enable:
::
+1 -1
View File
@@ -96,7 +96,7 @@ class BaseName:
@property
def module_path(self) -> Optional[Path]:
"""
Shows the file path of a module. e.g. ``/usr/lib/python3.14/os.py``
Shows the file path of a module. e.g. ``/usr/lib/python3.9/os.py``
"""
module = self._get_module_context()
if module.is_stub() or not module.is_compiled():
+1 -2
View File
@@ -1,6 +1,5 @@
import re
from textwrap import dedent
from typing import Any
from inspect import Parameter
from parso.python.token import PythonTokenTypes
@@ -266,7 +265,7 @@ class Completion:
elif type_ == 'for_stmt':
allowed_transitions.append('else')
completion_names: list[Any] = []
completion_names = []
kwargs_only = False
if any(t in allowed_transitions for t in (PythonTokenTypes.NAME,
+25 -29
View File
@@ -8,7 +8,7 @@ import hashlib
import filecmp
from collections import namedtuple
from shutil import which
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from jedi.cache import memoize_method, time_cache
from jedi.inference.compiled.subprocess import CompiledSubprocess, \
@@ -22,7 +22,7 @@ if TYPE_CHECKING:
_VersionInfo = namedtuple('VersionInfo', 'major minor micro') # type: ignore[name-match]
_SUPPORTED_PYTHONS = ['3.13', '3.12', '3.11', '3.10']
_SUPPORTED_PYTHONS = ['3.13', '3.12', '3.11', '3.10', '3.9', '3.8']
_SAFE_PATHS = ['/usr/bin', '/usr/local/bin']
_CONDA_VAR = 'CONDA_PREFIX'
_CURRENT_VERSION = '%s.%s' % (sys.version_info.major, sys.version_info.minor)
@@ -36,9 +36,6 @@ class InvalidPythonEnvironment(Exception):
class _BaseEnvironment:
version_info: Any
executable: Any
@memoize_method
def get_grammar(self):
version_string = '%s.%s' % (self.version_info.major, self.version_info.minor)
@@ -254,7 +251,7 @@ def get_cached_default_environment():
# /path/to/env so we need to fully resolve the paths in order to
# compare them.
if var and os.path.realpath(var) != os.path.realpath(environment.path):
_get_cached_default_environment.clear_cache() # type: ignore[attr-defined]
_get_cached_default_environment.clear_cache()
return _get_cached_default_environment()
return environment
@@ -358,7 +355,7 @@ def get_system_environment(version, *, env_vars=None):
return SameEnvironment()
return Environment(exe)
if sys.platform == "win32":
if os.name == 'nt':
for exe in _get_executables_from_windows_registry(version):
try:
return Environment(exe, env_vars=env_vars)
@@ -386,7 +383,7 @@ def _get_executable_path(path, safe=True):
Returns None if it's not actually a virtual env.
"""
if sys.platform == "win32":
if os.name == 'nt':
pythons = [os.path.join(path, 'Scripts', 'python.exe'), os.path.join(path, 'python.exe')]
else:
pythons = [os.path.join(path, 'bin', 'python')]
@@ -400,28 +397,27 @@ def _get_executable_path(path, safe=True):
return python
if sys.platform == "win32":
def _get_executables_from_windows_registry(version):
import winreg
def _get_executables_from_windows_registry(version):
import winreg
# TODO: support Python Anaconda.
sub_keys = [
r'SOFTWARE\Python\PythonCore\{version}\InstallPath',
r'SOFTWARE\Wow6432Node\Python\PythonCore\{version}\InstallPath',
r'SOFTWARE\Python\PythonCore\{version}-32\InstallPath',
r'SOFTWARE\Wow6432Node\Python\PythonCore\{version}-32\InstallPath'
]
for root_key in [winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE]:
for sub_key in sub_keys:
sub_key = sub_key.format(version=version)
try:
with winreg.OpenKey(root_key, sub_key) as key:
prefix = winreg.QueryValueEx(key, '')[0]
exe = os.path.join(prefix, 'python.exe')
if os.path.isfile(exe):
yield exe
except WindowsError:
pass
# TODO: support Python Anaconda.
sub_keys = [
r'SOFTWARE\Python\PythonCore\{version}\InstallPath',
r'SOFTWARE\Wow6432Node\Python\PythonCore\{version}\InstallPath',
r'SOFTWARE\Python\PythonCore\{version}-32\InstallPath',
r'SOFTWARE\Wow6432Node\Python\PythonCore\{version}-32\InstallPath'
]
for root_key in [winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE]:
for sub_key in sub_keys:
sub_key = sub_key.format(version=version)
try:
with winreg.OpenKey(root_key, sub_key) as key:
prefix = winreg.QueryValueEx(key, '')[0]
exe = os.path.join(prefix, 'python.exe')
if os.path.isfile(exe):
yield exe
except WindowsError:
pass
def _assert_safe(executable_path, safe):
+1 -1
View File
@@ -41,7 +41,7 @@ def imitate_pydoc(string):
try:
# is a tuple now
label, related = string # type: ignore[misc]
label, related = string
except TypeError:
return ''
+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::
$ python
Python 3.14.0+ (default, Jul 20 2020, 22:15:08)
Python 3.9.2+ (default, Jul 20 2020, 22:15:08)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
+1 -1
View File
@@ -93,7 +93,7 @@ def time_cache(seconds):
cache[key] = time.time(), result
return result
wrapper.clear_cache = lambda: cache.clear() # type: ignore[attr-defined]
wrapper.clear_cache = lambda: cache.clear()
return wrapper
return decorator
+1 -1
View File
@@ -36,7 +36,7 @@ try:
# pytest resets the stream at the end - causes troubles. Since
# after every output the stream is reset automatically we don't
# need this.
initialise.atexit_done = True # type: ignore[attr-defined]
initialise.atexit_done = True
try:
init(strip=False)
except Exception:
-3
View File
@@ -1,5 +1,4 @@
import os
from typing import Any
from parso import file_io
@@ -59,8 +58,6 @@ class FolderIO(AbstractFolderIO):
class FileIOFolderMixin:
path: Any
def get_parent_folder(self):
return FolderIO(os.path.dirname(self.path))
-4
View File
@@ -62,8 +62,6 @@ I need to mention now that lazy type inference is really good because it
only *inferes* what needs to be *inferred*. All the statements and modules
that are not used are just being ignored.
"""
from typing import Any
import parso
from jedi.file_io import FileIO
@@ -84,8 +82,6 @@ from jedi.plugins import plugin_manager
class InferenceState:
analysis_modules: "list[Any]"
def __init__(self, project, environment=None, script_path=None):
if environment is None:
environment = project.get_environment()
+1 -4
View File
@@ -1,6 +1,5 @@
import re
from itertools import zip_longest
from typing import Any
from parso.python import tree
@@ -135,7 +134,7 @@ class _AbstractArgumentsMixin:
class AbstractArguments(_AbstractArgumentsMixin):
context = None
argument_node: Any = None
argument_node = None
trailer = None
@@ -165,8 +164,6 @@ def unpack_arglist(arglist):
class TreeArguments(AbstractArguments):
context: Any
def __init__(self, inference_state, context, argument_node, trailer=None):
"""
:param argument_node: May be an argument_node or a list of nodes.
+6 -25
View File
@@ -9,7 +9,6 @@ just one.
from functools import reduce
from operator import add
from itertools import zip_longest
from typing import TYPE_CHECKING, Any
from parso.python.tree import Name
@@ -22,25 +21,12 @@ from jedi.cache import memoize_method
sentinel = object()
if TYPE_CHECKING:
from jedi.inference import InferenceState
class HasNoContext(Exception):
pass
class HelperValueMixin:
parent_context: Any
inference_state: "InferenceState"
name: Any
get_filters: Any
is_stub: Any
py__getattribute__alternatives: Any
py__iter__: Any
py__mro__: Any
_as_context: Any
def get_root_context(self):
value = self
if value.parent_context is None:
@@ -351,16 +337,11 @@ class _ValueWrapperBase(HelperValueMixin):
class LazyValueWrapper(_ValueWrapperBase):
if TYPE_CHECKING:
@property
def _wrapped_value(self) -> Any:
return
else:
@safe_property
@memoize_method
def _wrapped_value(self):
with debug.increase_indent_cm('Resolve lazy value wrapper'):
return self._get_wrapped_value()
@safe_property
@memoize_method
def _wrapped_value(self):
with debug.increase_indent_cm('Resolve lazy value wrapper'):
return self._get_wrapped_value()
def __repr__(self):
return '<%s>' % (self.__class__.__name__)
@@ -518,7 +499,7 @@ class ValueSet:
return ValueSet.from_sets(_getitem(c, *args, **kwargs) for c in self._set)
def try_merge(self, function_name):
value_set = ValueSet([])
value_set = self.__class__([])
for c in self._set:
try:
method = getattr(c, function_name)
+2 -2
View File
@@ -39,7 +39,7 @@ def _is_type(obj):
def _shadowed_dict(klass):
dict_attr = type.__dict__["__dict__"] # type: ignore[index]
dict_attr = type.__dict__["__dict__"]
for entry in _static_getmro(klass):
try:
class_dict = dict_attr.__get__(entry)["__dict__"]
@@ -54,7 +54,7 @@ def _shadowed_dict(klass):
def _static_getmro(klass):
mro = type.__dict__['__mro__'].__get__(klass) # type: ignore[index]
mro = type.__dict__['__mro__'].__get__(klass)
if not isinstance(mro, (tuple, list)):
# There are unfortunately no tests for this, I was not able to
# reproduce this in pure Python. However should still solve the issue
@@ -33,7 +33,7 @@ import traceback
import weakref
from functools import partial
from threading import Thread
from typing import Dict, TYPE_CHECKING, Any
from typing import Dict, TYPE_CHECKING
from jedi._compatibility import pickle_dump, pickle_load
from jedi import debug
@@ -52,7 +52,7 @@ PICKLE_PROTOCOL = 4
def _GeneralizedPopen(*args, **kwargs):
if sys.platform == "win32":
if os.name == 'nt':
try:
# Was introduced in Python 3.7.
CREATE_NO_WINDOW = subprocess.CREATE_NO_WINDOW
@@ -104,8 +104,6 @@ def _cleanup_process(process, thread):
class _InferenceStateProcess:
get_compiled_method_return: Any
def __init__(self, inference_state: 'InferenceState') -> None:
self._inference_state_weakref = weakref.ref(inference_state)
self._handles: Dict[int, AccessHandle] = {}
@@ -158,10 +158,7 @@ def _find_module(string, path=None, full_name=None, is_global_search=True):
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, # type: ignore[union-attr]
)
implicit_ns_info = ImplicitNSInfo(full_name, spec.submodule_search_locations._path)
return implicit_ns_info, True
break
+2 -2
View File
@@ -592,7 +592,7 @@ def create_from_name(inference_state, compiled_value, name):
value = create_cached_compiled_value(
inference_state,
access_path,
parent_context=None if value is None else value.as_context(), # type: ignore # TODO
parent_context=None if value is None else value.as_context(),
)
return value
@@ -610,7 +610,7 @@ def create_from_access_path(inference_state, access_path):
value = create_cached_compiled_value(
inference_state,
access,
parent_context=None if value is None else value.as_context() # type: ignore # TODO
parent_context=None if value is None else value.as_context()
)
return value
+2 -10
View File
@@ -1,7 +1,7 @@
from abc import abstractmethod
from contextlib import contextmanager
from pathlib import Path
from typing import Optional, Any
from typing import Optional
from parso.python.tree import Name
@@ -16,8 +16,6 @@ from jedi import parser_utils
class AbstractContext:
# Must be defined: inference_state and tree_node and parent_context as an attribute/property
tree_node: Any
parent_context: Any
def __init__(self, inference_state):
self.inference_state = inference_state
@@ -220,13 +218,6 @@ class ValueContext(AbstractContext):
class TreeContextMixin:
tree_node: Any
is_module: Any
get_value: Any
inference_state: Any
is_class: Any
parent_context: Any
def infer_node(self, node):
from jedi.inference.syntax_tree import infer_node
return infer_node(self, node)
@@ -309,6 +300,7 @@ class TreeContextMixin:
class FunctionContext(TreeContextMixin, ValueContext):
def get_filters(self, until_position=None, origin_scope=None):
yield ParserTreeFilter(
self.inference_state,
parent_context=self,
until_position=until_position,
origin_scope=origin_scope
+1 -1
View File
@@ -16,6 +16,6 @@ class DocstringModuleContext(ModuleContext):
super().__init__(module_value)
self._in_module_context = in_module_context
def get_filters(self, until_position=None, origin_scope=None):
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()
+2 -2
View File
@@ -109,7 +109,7 @@ def _search_function_arguments(module_context, funcdef, string_name):
if string_name == '__init__':
cls = get_parent_scope(funcdef)
if cls.type == 'classdef':
string_name = cls.name.value # type: ignore[union-attr]
string_name = cls.name.value
compare_node = cls
found_arguments = False
@@ -203,7 +203,7 @@ def _check_name_for_execution(inference_state, context, compare_node, name, trai
# Here we're trying to find decorators by checking the first
# parameter. It's not very generic though. Should find a better
# solution that also applies to nested decorators.
param_names = value.parent_context.get_param_names() # type: ignore[attr-defined]
param_names = value.parent_context.get_param_names()
if len(param_names) != 1:
continue
values = param_names[0].infer()
+3 -6
View File
@@ -3,7 +3,7 @@ Filters are objects that you can use to filter names in different scopes. They
are needed for name resolution.
"""
from abc import abstractmethod
from typing import MutableMapping, Type, Any
from typing import List, MutableMapping, Type
import weakref
from parso.python.tree import Name, UsedNamesMapping
@@ -16,8 +16,8 @@ from jedi.inference.utils import to_list
from jedi.inference.names import TreeNameDefinition, ParamName, \
AnonymousParamName, AbstractNameDefinition, NameWrapper
_definition_name_cache: 'MutableMapping[UsedNamesMapping, dict[str, tuple[Name, ...]]]' \
= weakref.WeakKeyDictionary()
_definition_name_cache: MutableMapping[UsedNamesMapping, List[Name]]
_definition_name_cache = weakref.WeakKeyDictionary()
class AbstractFilter:
@@ -346,9 +346,6 @@ class _OverwriteMeta(type):
class _AttributeOverwriteMixin:
overwritten_methods: Any
_wrapped_value: Any
def get_filters(self, *args, **kwargs):
yield SpecialMethodFilter(self, self.overwritten_methods, self._wrapped_value)
yield from self._wrapped_value.get_filters(*args, **kwargs)
+2 -2
View File
@@ -195,7 +195,7 @@ class GenericClass(DefineGenericBaseClass, ClassMixin):
@to_list
def py__bases__(self):
for base in self._wrapped_value.py__bases__(): # type: ignore[attr-defined]
for base in self._wrapped_value.py__bases__():
yield _LazyGenericBaseClass(self, base, self._generics_manager)
def _create_instance_with_generics(self, generics_manager):
@@ -384,7 +384,7 @@ class BaseTypingValue(LazyValueWrapper):
return _PseudoTreeNameClass(self.parent_context, self._tree_name)
def get_signatures(self):
return self._wrapped_value.get_signatures() # type: ignore[attr-defined]
return self._wrapped_value.get_signatures()
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self._tree_name.value)
-9
View File
@@ -2,7 +2,6 @@
This module is about generics, like the `int` in `List[int]`. It's not about
the Generic class.
"""
from abc import abstractmethod
from jedi import debug
from jedi.cache import memoize_method
@@ -25,14 +24,6 @@ def _resolve_forward_references(context, value_set):
class _AbstractGenericManager:
@abstractmethod
def __getitem__(self, index):
raise NotImplementedError
@abstractmethod
def to_tuple(self):
raise NotImplementedError
def get_index_and_execute(self, index):
try:
return self[index].execute_annotation()
-3
View File
@@ -6,7 +6,6 @@ values.
This file deals with all the typing.py cases.
"""
import itertools
from typing import Any
from jedi import debug
from jedi.inference.compiled import builtin_from_name, create_simple_object
@@ -187,8 +186,6 @@ class ProxyTypingValue(BaseTypingValue):
class _TypingClassMixin(ClassMixin):
_tree_name: Any
def py__bases__(self):
return [LazyKnownValues(
self.inference_state.builtins_module.py__getattribute__('object')
+2 -2
View File
@@ -5,12 +5,12 @@ import os
from itertools import chain
from contextlib import contextmanager
from parso import tree
from parso.python import tree
def is_stdlib_path(path):
# Python standard library paths look like this:
# /usr/lib/python3.14/...
# /usr/lib/python3.9/...
# TODO The implementation below is probably incorrect and not complete.
parts = path.parts
if 'dist-packages' in parts or 'site-packages' in parts:
+5 -5
View File
@@ -370,16 +370,16 @@ def import_module_by_names(inference_state, import_names, sys_path=None,
i.value if isinstance(i, tree.Name) else i
for i in import_names
)
base = [None]
value_set = [None]
for i, name in enumerate(import_names):
base = value_set = ValueSet.from_sets([
value_set = ValueSet.from_sets([
import_module(
inference_state,
str_import_names[:i+1],
parent_module_value,
sys_path,
prefer_stubs=prefer_stubs, # type: ignore[call-arg]
) for parent_module_value in base
prefer_stubs=prefer_stubs,
) for parent_module_value in value_set
])
if not value_set:
message = 'No module named ' + '.'.join(str_import_names)
@@ -474,7 +474,7 @@ def _load_python_module(inference_state, file_io,
)
def _load_builtin_module(inference_state, import_names, sys_path):
def _load_builtin_module(inference_state, import_names=None, sys_path=None):
project = inference_state.project
if sys_path is None:
sys_path = inference_state.get_sys_path()
+10 -21
View File
@@ -1,8 +1,9 @@
from abc import abstractmethod
from inspect import Parameter
from typing import Optional, Tuple, Any
from typing import Optional, Tuple
from jedi.parser_utils import find_statement_documentation, clean_scope_docstring
from jedi.inference.utils import unite
from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference.cache import inference_state_method_cache
from jedi.inference import docstrings
@@ -36,6 +37,7 @@ class AbstractNameDefinition:
def infer(self):
raise NotImplementedError
@abstractmethod
def goto(self):
# Typically names are already definitions and therefore a goto on that
# name will always result on itself.
@@ -103,9 +105,6 @@ class AbstractArbitraryName(AbstractNameDefinition):
class AbstractTreeName(AbstractNameDefinition):
tree_name: Any
parent_context: Any
def __init__(self, parent_context, tree_name):
self.parent_context = parent_context
self.tree_name = tree_name
@@ -195,11 +194,10 @@ class AbstractTreeName(AbstractNameDefinition):
new_dotted = deep_ast_copy(par)
new_dotted.children[index - 1:] = []
values = context.infer_node(new_dotted)
return [
n
return unite(
value.goto(name, name_context=context)
for value in values
for n in value.goto(name, name_context=context)
]
)
if node_type == 'trailer' and par.children[0] == '.':
values = infer_call_of_leaf(context, name, cut_own_trailer=True)
@@ -224,9 +222,6 @@ class AbstractTreeName(AbstractNameDefinition):
class ValueNameMixin:
_value: Any
parent_context: Any
def infer(self):
return ValueSet([self._value])
@@ -245,7 +240,7 @@ class ValueNameMixin:
def get_root_context(self):
if self.parent_context is None: # A module
return self._value.as_context()
return super().get_root_context() # type: ignore
return super().get_root_context()
def get_defining_qualified_value(self):
context = self.parent_context
@@ -362,16 +357,14 @@ class TreeNameDefinition(AbstractTreeName):
class _ParamMixin:
get_kind: Any
def maybe_positional_argument(self, include_star=True):
options: list[int] = [Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD]
options = [Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD]
if include_star:
options.append(Parameter.VAR_POSITIONAL)
return self.get_kind() in options
def maybe_keyword_argument(self, include_stars=True):
options: list[int] = [Parameter.KEYWORD_ONLY, Parameter.POSITIONAL_OR_KEYWORD]
options = [Parameter.KEYWORD_ONLY, Parameter.POSITIONAL_OR_KEYWORD]
if include_stars:
options.append(Parameter.VAR_KEYWORD)
return self.get_kind() in options
@@ -636,10 +629,6 @@ class NameWrapper:
class StubNameMixin:
api_type: str
tree_name: Any
infer: Any
def py__doc__(self):
from jedi.inference.gradual.conversion import convert_names
# Stubs are not complicated and we can just follow simple statements
@@ -651,7 +640,7 @@ class StubNameMixin:
names = convert_names(names, prefer_stub_to_compiled=False)
if self in names:
return super().py__doc__() # type: ignore
return super().py__doc__()
else:
# We have signatures ourselves in stubs, so don't use signatures
# from the implementation.
-7
View File
@@ -1,5 +1,4 @@
from inspect import Parameter
from typing import Any
from jedi.cache import memoize_method
from jedi import debug
@@ -7,10 +6,6 @@ from jedi import parser_utils
class _SignatureMixin:
get_param_names: Any
name: Any
annotation_string: Any
def to_string(self):
def param_strings():
is_positional = False
@@ -41,8 +36,6 @@ class _SignatureMixin:
class AbstractSignature(_SignatureMixin):
_function_value: Any
def __init__(self, value, is_bound=False):
self.value = value
self.is_bound = is_bound
+1 -1
View File
@@ -89,7 +89,6 @@ def infer_node(context, element):
if isinstance(context, CompForContext):
return _infer_node(context, element)
name_dicts = [{}]
if_stmt = element
while if_stmt is not None:
if_stmt = if_stmt.parent
@@ -105,6 +104,7 @@ def infer_node(context, element):
if predefined_if_name_dict is None and if_stmt \
and if_stmt.type == 'if_stmt' and context.inference_state.is_analysis:
if_stmt_test = if_stmt.children[1]
name_dicts = [{}]
# If we already did a check, we don't want to do it again -> If
# value.predefined_names is filled, we stop.
# We don't want to check the if stmt itself, it's just about
+1
View File
@@ -74,6 +74,7 @@ class PushBackIterator:
def __init__(self, iterator):
self.pushes = []
self.iterator = iterator
self.current = None
def push_back(self, value):
self.pushes.append(value)
-6
View File
@@ -1,5 +1,3 @@
from typing import Any
from jedi import debug
from jedi.inference.cache import inference_state_method_cache, CachedMetaClass
from jedi.inference import compiled
@@ -55,10 +53,6 @@ class FunctionAndClassBase(TreeValue):
class FunctionMixin:
api_type = 'function'
tree_node: Any
py__class__: Any
as_context: Any
get_signature_functions: Any
def get_filters(self, origin_scope=None):
cls = self.py__class__()
-4
View File
@@ -1,5 +1,4 @@
from abc import abstractproperty
from typing import Any
from jedi import debug
from jedi import settings
@@ -188,9 +187,6 @@ class CompiledInstance(AbstractInstanceValue):
class _BaseTreeInstance(AbstractInstanceValue):
get_defined_names: Any
_arguments: Any
@property
def array_type(self):
name = self.class_value.py__name__()
+1 -17
View File
@@ -2,8 +2,6 @@
Contains all classes and functions to deal with lists, dicts, generators and
iterators in general.
"""
from typing import Any
from jedi.inference import compiled
from jedi.inference import analysis
from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \
@@ -22,9 +20,6 @@ from jedi.inference.value.dynamic_arrays import check_array_additions
class IterableMixin:
py__iter__: Any
inference_state: Any
def py__next__(self, contextualized_node=None):
return self.py__iter__(contextualized_node)
@@ -133,12 +128,6 @@ def comprehension_from_atom(inference_state, value, atom):
class ComprehensionMixin:
_defining_context: Any
_entry_node: Any
array_type: Any
_value_node: Any
_sync_comp_for_node: Any
@inference_state_method_cache()
def _get_comp_for_context(self, parent_context, comp_for):
return CompForContext(parent_context, comp_for)
@@ -187,8 +176,6 @@ class ComprehensionMixin:
class _DictMixin:
get_mapping_item_values: Any
def _get_generics(self):
return tuple(c_set.py__class__() for c_set in self.get_mapping_item_values())
@@ -233,7 +220,7 @@ class Sequence(LazyAttributeOverwrite, IterableMixin):
class _BaseComprehension(ComprehensionMixin):
def __init__(self, inference_state, defining_context, sync_comp_for_node, entry_node):
assert sync_comp_for_node.type == 'sync_comp_for'
super().__init__(inference_state) # type: ignore[call-arg]
super().__init__(inference_state)
self._defining_context = defining_context
self._sync_comp_for_node = sync_comp_for_node
self._entry_node = entry_node
@@ -261,9 +248,6 @@ class GeneratorComprehension(_BaseComprehension, GeneratorBase):
class _DictKeyMixin:
_dict_keys: Any
_dict_values: Any
# TODO merge with _DictMixin?
def get_mapping_item_values(self):
return self._dict_keys(), self._dict_values()
+1 -16
View File
@@ -38,7 +38,7 @@ py__doc__() Returns the docstring for a value.
"""
from __future__ import annotations
from typing import List, Optional, Tuple, TYPE_CHECKING, Any
from typing import List, Optional, Tuple
from jedi import debug
from jedi.parser_utils import get_cached_parent_scope, expr_is_dotted, \
@@ -61,9 +61,6 @@ from inspect import Parameter
from jedi.inference.names import BaseTreeParamName
from jedi.inference.signature import AbstractSignature
if TYPE_CHECKING:
from jedi.inference import InferenceState
class ClassName(TreeNameDefinition):
def __init__(self, class_value, tree_name, name_context, apply_decorators):
@@ -200,15 +197,6 @@ def get_dataclass_param_names(cls) -> List[DataclassParamName]:
class ClassMixin:
tree_node: Any
parent_context: Any
inference_state: InferenceState
py__bases__: Any
get_metaclasses: Any
get_metaclass_filters: Any
get_metaclass_signatures: Any
list_type_vars: Any
def is_class(self):
return True
@@ -694,9 +682,6 @@ class ClassValue(ClassMixin, FunctionAndClassBase, metaclass=CachedMetaClass):
"""
bases_arguments = self._get_bases_arguments()
if bases_arguments is None:
return None
if bases_arguments.argument_node.type != "arglist":
# If it is not inheriting from the base model and having
# extra parameters, then init behavior is not changed.
+1 -13
View File
@@ -1,6 +1,6 @@
import os
from pathlib import Path
from typing import Optional, TYPE_CHECKING, Any
from typing import Optional
from jedi.inference.cache import inference_state_method_cache
from jedi.inference.names import AbstractNameDefinition, ModuleName
@@ -13,9 +13,6 @@ from jedi.inference.compiled import create_simple_object
from jedi.inference.base_value import ValueSet
from jedi.inference.context import ModuleContext
if TYPE_CHECKING:
from jedi.inference import InferenceState
class _ModuleAttributeName(AbstractNameDefinition):
"""
@@ -38,11 +35,6 @@ class _ModuleAttributeName(AbstractNameDefinition):
class SubModuleDictMixin:
inference_state: "InferenceState"
is_package: Any
py__path__: Any
as_context: Any
@inference_state_method_cache()
def sub_modules_dict(self):
"""
@@ -65,10 +57,6 @@ class SubModuleDictMixin:
class ModuleMixin(SubModuleDictMixin):
_module_name_class = ModuleName
tree_node: Any
string_names: Any
sub_modules_dict: Any
py__file__: Any
def get_filters(self, origin_scope=None):
yield MergedFilter(
+1 -1
View File
@@ -259,7 +259,7 @@ def get_parent_scope(node, include_flows=False):
# the if, but the parent of the if.
if not (scope.type == 'if_stmt'
and any(n.start_pos <= node.start_pos < n.end_pos
for n in scope.get_test_nodes())): # type: ignore[attr-defined]
for n in scope.get_test_nodes())):
return scope
scope = scope.parent
+1 -2
View File
@@ -2,7 +2,6 @@
Module is used to infer Django model fields.
"""
from inspect import Parameter
from typing import Any
from jedi import debug
from jedi.inference.cache import inference_state_function_cache
@@ -141,7 +140,7 @@ def _new_dict_filter(cls, is_instance):
include_metaclasses=False,
include_type_when_class=False)
)
dct: dict[str, Any] = {
dct = {
name.string_name: DjangoModelName(cls, name, is_instance)
for filter_ in reversed(filters)
for name in filter_.values()
+21 -7
View File
@@ -138,14 +138,28 @@ def _find_pytest_plugin_modules() -> List[List[str]]:
See https://docs.pytest.org/en/stable/how-to/writing_plugins.html#setuptools-entry-points
"""
from importlib.metadata import entry_points
if sys.version_info >= (3, 8):
from importlib.metadata import entry_points
if sys.version_info >= (3, 10):
pytest_entry_points = entry_points(group="pytest11")
else:
pytest_entry_points = entry_points().get("pytest11", ())
if sys.version_info >= (3, 9):
return [ep.module.split(".") for ep in pytest_entry_points]
else:
# Python 3.8 doesn't have `EntryPoint.module`. Implement equivalent
# to what Python 3.9 does (with additional None check to placate `mypy`)
matches = [
ep.pattern.match(ep.value)
for ep in pytest_entry_points
]
return [x.group('module').split(".") for x in matches if x]
if sys.version_info >= (3, 10):
pytest_entry_points = entry_points(group="pytest11")
else:
pytest_entry_points = entry_points().get("pytest11", ())
return [ep.module.split(".") for ep in pytest_entry_points]
from pkg_resources import iter_entry_points
return [ep.module_name.split(".") for ep in iter_entry_points(group="pytest11")]
@inference_state_method_cache()
@@ -178,7 +192,7 @@ def _iter_pytest_modules(module_context, skip_own_module=False):
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: # type: ignore # TODO
if last_folder is not None and folder.path == last_folder.path:
break
last_folder = folder # keep track of the last found parent name
+1 -1
View File
@@ -134,7 +134,7 @@ def execute(callback):
except KeyError:
pass
else:
return func(value, arguments=arguments, callback=call) # type: ignore
return func(value, arguments=arguments, callback=call)
return call()
return wrapper
+31 -13
View File
@@ -1,14 +1,32 @@
[tool.zuban]
strict = true
enable_error_code = ["ignore-without-code"]
# Revert some --strict specific flags:
allow_untyped_calls = true
allow_untyped_defs = true
allow_incomplete_defs = true
allow_untyped_globals = true
untyped_strict_optional = false
implicit_reexport = true
[tool.mypy]
# Exclude our copies of external stubs
exclude = "^jedi/third_party|^test/(completion|refactor|static_analysis|examples)/"
exclude = "^jedi/third_party"
show_error_codes = true
enable_error_code = "ignore-without-code"
# Ensure generics are explicit about what they are (e.g: `List[str]` rather than
# just `List`)
disallow_any_generics = true
disallow_subclassing_any = true
# Avoid creating future gotchas emerging from bad typing
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
warn_unused_configs = true
warn_unreachable = true
# Require values to be explicitly re-exported; this makes things easier for
# Flake8 too and avoids accidentally importing thing from the "wrong" place
# (which helps avoid circular imports)
implicit_reexport = false
strict_equality = true
[[tool.mypy.overrides]]
# Various __init__.py files which contain re-exports we want to implicitly make.
module = ["jedi", "jedi.inference.compiled", "jedi.inference.value", "parso"]
implicit_reexport = true
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python
"""
Profile a piece of Python code with ``cProfile`` that uses the diff parser.
Usage:
profile.py <file> [-d] [-s <sort>]
profile.py -h | --help
Options:
-h --help Show this screen.
-d --debug Enable Jedi internal debugging.
-s <sort> Sort the profile results, e.g. cumtime, name [default: time].
"""
import cProfile
from docopt import docopt
from jedi.parser.python import load_grammar
from jedi.parser.diff import DiffParser
from jedi.parser.python import ParserWithRecovery
from jedi.common import splitlines
import jedi
def run(parser, lines):
diff_parser = DiffParser(parser)
diff_parser.update(lines)
# Make sure used_names is loaded
parser.module.used_names
def main(args):
if args['--debug']:
jedi.set_debug_function(notices=True)
with open(args['<file>']) as f:
code = f.read()
grammar = load_grammar()
parser = ParserWithRecovery(grammar, code)
# Make sure used_names is loaded
parser.module.used_names
code = code + '\na\n' # Add something so the diff parser needs to run.
lines = splitlines(code, keepends=True)
cProfile.runctx('run(parser, lines)', globals(), locals(), sort=args['-s'])
if __name__ == '__main__':
args = docopt(__doc__)
main(args)
+1 -1
View File
@@ -13,7 +13,7 @@ Note: This requires the psutil library, available on PyPI.
import time
import sys
import os
import psutil # type: ignore[import-untyped]
import psutil
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__) + '/..'))
import jedi
+1 -1
View File
@@ -56,7 +56,7 @@ def main(args):
run(code, i, infer=infer)
if args['--precision']:
pstats.f8 = f8 # type: ignore[attr-defined] # TODO this does not seem to exist?!
pstats.f8 = f8
jedi.set_debug_function(notices=args['--debug'])
if args['--omit']:
+2 -2
View File
@@ -17,11 +17,11 @@ import sys
try:
import urllib.request as urllib2
except ImportError:
import urllib2 # type: ignore[import-not-found, no-redef]
import urllib2
import gc
from os.path import abspath, dirname
import objgraph # type: ignore[import-untyped]
import objgraph
sys.path.insert(0, dirname(dirname(abspath(__file__))))
import jedi
+10 -5
View File
@@ -35,11 +35,11 @@ setup(name='jedi',
keywords='python completion refactoring vim',
long_description=readme,
packages=find_packages(exclude=['test', 'test.*']),
python_requires='>=3.10',
python_requires='>=3.8',
# Python 3.13 grammars are added to parso in 0.8.4
install_requires=['parso>=0.8.6,<0.9.0'],
install_requires=['parso>=0.8.5,<0.9.0'],
extras_require={
'dev': [
'testing': [
'pytest<9.0.0',
# docopt for sith doctests
'docopt',
@@ -48,9 +48,12 @@ setup(name='jedi',
'Django',
'attrs',
'typing_extensions',
],
'qa': [
# latest version on 2025-06-16
'flake8==7.1.2',
'zuban==0.7.0',
'flake8==7.2.0',
# latest version supporting Python 3.6
'mypy==1.16',
# Arbitrary pins, latest at the time of pinning
'types-setuptools==80.9.0.20250529',
],
@@ -93,6 +96,8 @@ setup(name='jedi',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
+2
View File
@@ -35,6 +35,7 @@ Usage:
Options:
-h --help Show this screen.
--record=<file> Exceptions are recorded in here [default: record.json].
-f, --fs-cache By default, file system cache is off for reproducibility.
-n, --maxtries=<nr> Maximum of random tries [default: 100]
-d, --debug Jedi print debugging when an error is raised.
-s Shows the path/line numbers of every completion before it starts.
@@ -186,6 +187,7 @@ def main(arguments):
'pudb' if arguments['--pudb'] else None
record = arguments['--record']
jedi.settings.use_filesystem_cache = arguments['--fs-cache']
if arguments['--debug']:
jedi.set_debug_function()
+1
View File
@@ -1,6 +1,7 @@
# For assignment expressions / named expressions / walrus operators / whatever
# they are called.
# python >= 3.8
b = (a:=1, a)
#? int()
+2
View File
@@ -476,6 +476,8 @@ dynamic_annotation('')
# TypeDict
# -------------------------
# python >= 3.8
class Foo(typing.TypedDict):
foo: str
bar: typing.List[float]
+2
View File
@@ -1,3 +1,5 @@
# python >= 3.9
from typing import Annotated
# This is just a dummy and very meaningless thing to use with to the Annotated
@@ -1,3 +1,5 @@
# python >= 3.8
def positional_only_call(a, /, b):
#? str()
a
+2
View File
@@ -459,6 +459,8 @@ X().just_partial('')[0]
#? str()
X().just_partial('')[1]
# python >= 3.8
@functools.lru_cache
def x() -> int: ...
@functools.lru_cache()
+1 -1
View File
@@ -14,7 +14,7 @@ from jedi.api.interpreter import MixedModuleContext
# For interpreter tests sometimes the path of this directory is in the sys
# path, which we definitely don't want. So just remove it globally.
try:
sys.path.remove(str(helpers.test_dir))
sys.path.remove(helpers.test_dir)
except ValueError:
pass
+1 -1
View File
@@ -2,7 +2,7 @@
import sys
sys.path[0:0] = [
'/usr/lib/python3.14/site-packages',
'/usr/lib/python3.8/site-packages',
'/tmp/.buildout/eggs/important_package.egg'
]
+1 -3
View File
@@ -168,8 +168,6 @@ class BaseTestCase(object):
class IntegrationTestCase(BaseTestCase):
source: str # Defined as a side effect
def __init__(self, test_type, correct, line_nr, column, start, line,
path=None, skip_version_info=None):
super().__init__(skip_version_info)
@@ -448,7 +446,7 @@ Options:
--pdb Enable pdb debugging on fail.
-d, --debug Enable text output debugging (please install ``colorama``).
--thirdparty Also run thirdparty tests (in ``completion/thirdparty``).
--env <dotted> A Python version, like 3.14, 3.13, etc.
--env <dotted> A Python version, like 3.9, 3.8, etc.
"""
if __name__ == '__main__':
import docopt
+1 -1
View File
@@ -523,7 +523,7 @@ def test_added_equals_to_params(Script):
def test_builtin_module_with_path(Script):
"""
This test simply tests if a module from /usr/lib/python3.14/lib-dynload/ has
This test simply tests if a module from /usr/lib/python3.8/lib-dynload/ has
a path or not. It shouldn't have a module_path, because that is just
confusing.
"""
+1 -2
View File
@@ -3,7 +3,6 @@ import os
from textwrap import dedent
from itertools import count
from pathlib import Path
from typing import Any
import pytest
@@ -302,7 +301,7 @@ def test_file_path_should_have_completions(Script):
assert Script('r"').complete() # See GH #1503
_dict_keys_completion_tests: "list[tuple[str, int | None, list[str | Any]]]" = [
_dict_keys_completion_tests = [
('ints[', 5, ['1', '50', Ellipsis]),
('ints[]', 5, ['1', '50', Ellipsis]),
('ints[1]', 5, ['1', '50', Ellipsis]),
+2 -2
View File
@@ -26,13 +26,13 @@ def test_find_system_environments():
@pytest.mark.parametrize(
'version',
['3.10', '3.11', '3.12', '3.13']
['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
)
def test_versions(version):
try:
env = get_system_environment(version)
except InvalidPythonEnvironment:
if version.replace('.', '') == str(sys.version_info[0]) + str(sys.version_info[1]):
if int(version.replace('.', '')) == str(sys.version_info[0]) + str(sys.version_info[1]):
# At least the current version has to work
raise
pytest.skip()
-3
View File
@@ -15,7 +15,6 @@ There are three kinds of test:
import textwrap
from unittest import TestCase
from typing import Any
import pytest
@@ -23,8 +22,6 @@ import jedi
class MixinTestFullName(object):
assertEqual: Any
operation = None
@pytest.fixture(autouse=True)
+13 -13
View File
@@ -78,7 +78,7 @@ def test_numpy_like_non_zero():
def test_nested_resolve():
class XX:
def x(): # type: ignore[misc]
def x():
pass
cls = get_completion('XX', locals())
@@ -92,7 +92,7 @@ def test_side_effect_completion():
Python code, however we want references to Python code as well. Therefore
we need some mixed kind of magic for tests.
"""
_GlobalNameSpace.SideEffectContainer.foo = 1 # type: ignore[attr-defined]
_GlobalNameSpace.SideEffectContainer.foo = 1
side_effect = get_completion('SideEffectContainer', _GlobalNameSpace.__dict__)
# It's a class that contains MixedObject.
@@ -166,7 +166,7 @@ def test_getitem_side_effects():
# Possible side effects here, should therefore not call this.
if True:
raise NotImplementedError()
return index # type: ignore[unreachable]
return index
foo = Foo2()
_assert_interpreter_complete('foo["asdf"].upper', locals(), ['upper'])
@@ -198,7 +198,7 @@ def test__getattr__completions(allow_unsafe_getattr, class_is_findable):
raise AttributeError(name)
def __dir__(self):
return ['foo', 'fbar'] + object.__dir__(self) # type: ignore[operator]
return ['foo', 'fbar'] + object.__dir__(self)
if not class_is_findable:
CompleteGetattr.__name__ = "something_somewhere"
@@ -388,7 +388,7 @@ def test_dir_magic_method(allow_unsafe_getattr):
raise AttributeError(name)
def __dir__(self):
return ['foo', 'bar'] + object.__dir__(self) # type: ignore[operator]
return ['foo', 'bar'] + object.__dir__(self)
itp = jedi.Interpreter("ca.", [{'ca': CompleteAttrs()}])
completions = itp.complete()
@@ -410,7 +410,7 @@ def test_dir_magic_method(allow_unsafe_getattr):
def test_name_not_findable():
class X():
if 0:
NOT_FINDABLE # type: ignore[unreachable] # noqa: F821
NOT_FINDABLE # noqa: F821
def hidden(self):
return
@@ -493,7 +493,7 @@ def test__wrapped__():
def test_illegal_class_instance():
class X:
__class__ = 1 # type: ignore[assignment]
__class__ = 1
X.__name__ = 'asdf'
d, = jedi.Interpreter('foo', [{'foo': X()}]).infer()
v, = d._name.infer()
@@ -537,7 +537,7 @@ def test_partial_signatures(code, expected, index):
def test_type_var():
"""This was an issue before, see Github #1369"""
x = typing.TypeVar('myvar') # type: ignore[misc]
x = typing.TypeVar('myvar')
def_, = jedi.Interpreter('x', [locals()]).infer()
assert def_.name == 'TypeVar'
@@ -576,7 +576,7 @@ def test_dict_completion(code, column, expected):
strs = {'asdf': 1, """foo""": 2, r'fbar': 3}
mixed = {1: 2, 1.10: 4, None: 6, r'a\sdf': 8, b'foo': 9}
class Inherited(dict): # type: ignore[type-arg]
class Inherited(dict):
pass
inherited = Inherited(blablu=3)
@@ -624,10 +624,10 @@ def test_dunders(class_is_findable, code, expected, allow_unsafe_getattr):
def __getitem__(self, key) -> int:
return 1
def __iter__(self, key) -> Iterator[str]: # type: ignore[empty-body]
def __iter__(self, key) -> Iterator[str]:
pass
def __next__(self, key) -> float: # type: ignore[empty-body]
def __next__(self, key) -> float:
pass
if not class_is_findable:
@@ -810,11 +810,11 @@ def test_try_to_use_return_annotation_for_property(class_is_findable):
raise BaseException
@property
def with_annotation_garbage1(self) -> 'asldjflksjdfljdslkjfsl': # type: ignore[name-defined] # noqa
def with_annotation_garbage1(self) -> 'asldjflksjdfljdslkjfsl': # noqa
return Hello()
@property
def with_annotation_garbage2(self) -> 'sdf & 8': # type: ignore[valid-type] # noqa
def with_annotation_garbage2(self) -> 'sdf$@@$5*+8': # noqa
return Hello()
@property
+1
View File
@@ -54,6 +54,7 @@ def test_rename_mod(Script, dir_with_content):
''').format(dir=dir_with_content)
@pytest.mark.skipif('sys.version_info[:2] < (3, 8)', message="Python 3.8 introduces dirs_exist_ok")
def test_namespace_package(Script, tmpdir):
origin = get_example_dir('implicit_namespace_package')
shutil.copytree(origin, tmpdir.strpath, dirs_exist_ok=True)
+1 -1
View File
@@ -13,7 +13,7 @@ class SomeClass:
def twice(self, b):
pass
def some_function(self):
def some_function():
pass
@@ -67,7 +67,7 @@ def test_path_from_sys_path_assignment(Script):
import sys
sys.path[0:0] = [
{os.path.abspath('/usr/lib/python3.14/site-packages')!r},
{os.path.abspath('/usr/lib/python3.8/site-packages')!r},
{os.path.abspath('/home/test/.buildout/eggs/important_package.egg')!r},
]
+2 -2
View File
@@ -11,14 +11,14 @@ import jedi
from ..helpers import test_dir
try:
import numpydoc # type: ignore[import-not-found] # NOQA
import numpydoc # NOQA
except ImportError:
numpydoc_unavailable = True
else:
numpydoc_unavailable = False
try:
import numpy # type: ignore[import-not-found] # NOQA
import numpy # NOQA
except ImportError:
numpy_unavailable = True
else:
+2 -5
View File
@@ -31,9 +31,7 @@ def test_get_signatures_stdlib(Script):
assert len(sigs[0].params) == 1
# TODO This is currently only checked on linux 64 bit platform and Python3.8,
# which we don't support anymore, this test should be rewritten (or the
# extension recreated).
# Check only on linux 64 bit platform and Python3.8.
@pytest.mark.parametrize('load_unsafe_extensions', [False, True])
@pytest.mark.skipif(
'sys.platform != "linux" or sys.maxsize <= 2**32 or sys.version_info[:2] != (3, 8)',
@@ -50,8 +48,7 @@ def test_init_extension_module(Script, load_unsafe_extensions):
`__init__.cpython-38m.so` by compiling it (create a virtualenv and run
`setup.py install`.
This is also why this test only runs on certain systems and a specific
Python version.
This is also why this test only runs on certain systems and Python 3.8.
"""
project = jedi.Project(get_example_dir(), load_unsafe_extensions=load_unsafe_extensions)
@@ -222,7 +222,7 @@ def test_goto_stubs_on_itself(Script, code, type_):
def test_module_exists_only_as_stub(Script):
try:
import redis # type: ignore[import-untyped] # noqa: F401
import redis # noqa: F401
except ImportError:
pass
else:
+2 -2
View File
@@ -30,13 +30,13 @@ def test_find_module_basic():
def test_find_module_package():
file_io, is_package = _find_module('json')
assert file_io.path.parts[-2:] == ('json', '__init__.py') # type: ignore[union-attr]
assert file_io.path.parts[-2:] == ('json', '__init__.py')
assert is_package is True
def test_find_module_not_package():
file_io, is_package = _find_module('io')
assert file_io.path.name == 'io.py' # type: ignore[union-attr]
assert file_io.path.name == 'io.py'
assert is_package is False
+4 -4
View File
@@ -56,10 +56,10 @@ def test_generics_methods(code, expected, class_findable):
class Reader(Generic[T]):
@classmethod
def read(cls) -> T:
return cls() # type: ignore[return-value]
return cls()
def method(self) -> T:
return 1 # type: ignore[return-value]
return 1
class Foo(Reader[str]):
def transform(self) -> int:
@@ -94,7 +94,7 @@ def test_signature():
pass
from inspect import Signature, Parameter
some_signature.__signature__ = Signature([ # type: ignore[attr-defined]
some_signature.__signature__ = Signature([
Parameter('bar', kind=Parameter.KEYWORD_ONLY, default=1)
])
@@ -105,7 +105,7 @@ def test_signature():
def test_compiled_signature_annotation_string():
import typing
def func(x: typing.Type, y: typing.Union[typing.Type, int]): # type: ignore[type-arg]
def func(x: typing.Type, y: typing.Union[typing.Type, int]):
pass
func.__name__ = 'not_func'
+4 -5
View File
@@ -2,7 +2,6 @@ from textwrap import dedent
from operator import eq, ge, lt
import re
import os
from typing import Any
import pytest
@@ -406,7 +405,7 @@ def test_wraps_signature(Script, code, signature):
],
)
def test_dataclass_signature(
Script, start, start_params, include_params, environment
Script, skip_pre_python37, start, start_params, include_params, environment
):
if environment.version_info < (3, 8):
# Final is not yet supported
@@ -449,7 +448,7 @@ def test_dataclass_signature(
assert price.name == price_type_infer
dataclass_transform_cases: list[Any] = [
dataclass_transform_cases = [
# Attributes on the decorated class and its base classes
# are not considered to be fields.
# 1/ Declare dataclass transformer
@@ -725,7 +724,7 @@ ids = [
'start, start_params, include_params', dataclass_transform_cases, ids=ids
)
def test_extensions_dataclass_transform_signature(
Script, start, start_params, include_params, environment
Script, skip_pre_python37, start, start_params, include_params, environment
):
has_typing_ext = bool(Script('import typing_extensions').infer())
if not has_typing_ext:
@@ -846,7 +845,7 @@ def test_dataclass_transform_signature(
],
ids=["define", "frozen", "define_customized", "define_subclass", "define_both"]
)
def test_attrs_signature(Script, start, start_params):
def test_attrs_signature(Script, skip_pre_python37, start, start_params):
has_attrs = bool(Script('import attrs').infer())
if not has_attrs:
raise pytest.skip("attrs needed in target environment to run this test")
@@ -67,7 +67,7 @@ def test_hex_values_in_docstring():
('lambda x, y, z: x + y * z\n', '<lambda>(x, y, z)')
])
def test_get_signature(code, signature):
node = parse(code, version='3.14').children[0]
node = parse(code, version='3.8').children[0]
if node.type == 'simple_stmt':
node = node.children[0]
assert parser_utils.get_signature(node) == signature
+6 -8
View File
@@ -1,9 +1,7 @@
from typing import Any
try:
import readline
except ImportError:
readline = False # type: ignore[assignment]
readline = False
import unittest
from jedi import utils
@@ -17,7 +15,7 @@ class TestSetupReadline(unittest.TestCase):
def setUp(self, *args, **kwargs):
super().setUp(*args, **kwargs)
self.namespace: Any = self.NameSpace()
self.namespace = self.NameSpace()
utils.setup_readline(self.namespace)
def complete(self, text):
@@ -49,8 +47,8 @@ class TestSetupReadline(unittest.TestCase):
def test_modules(self):
import sys
import os
self.namespace.sys = sys # type: ignore[attr-defined]
self.namespace.os = os # type: ignore[attr-defined]
self.namespace.sys = sys
self.namespace.os = os
try:
assert self.complete('os.path.join') == ['os.path.join']
@@ -60,8 +58,8 @@ class TestSetupReadline(unittest.TestCase):
c = {'os.' + d for d in dir(os) if d.startswith('ch')}
assert set(self.complete('os.ch')) == set(c)
finally:
del self.namespace.sys # type: ignore[attr-defined]
del self.namespace.os # type: ignore[attr-defined]
del self.namespace.sys
del self.namespace.os
def test_calls(self):
s = 'str(bytes'