From 195695edd348683a4ff955e7f892fe043d933858 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 21:01:03 +0100 Subject: [PATCH 01/14] Upgrade Django --- jedi/third_party/django-stubs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jedi/third_party/django-stubs b/jedi/third_party/django-stubs index fd057010..28afe598 160000 --- a/jedi/third_party/django-stubs +++ b/jedi/third_party/django-stubs @@ -1 +1 @@ -Subproject commit fd057010f6cbf176f57d1099e82be46d39b99cb9 +Subproject commit 28afe59881d796c483e10dde5169ee7cea5eef3d From e580d1f4d909e160ded40403f6efd152459d63ab Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 21:27:00 +0100 Subject: [PATCH 02/14] Fix a stub docs issue --- jedi/inference/names.py | 6 ++++++ test/test_api/test_documentation.py | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/jedi/inference/names.py b/jedi/inference/names.py index f90f9749..3e17ecd8 100644 --- a/jedi/inference/names.py +++ b/jedi/inference/names.py @@ -341,6 +341,12 @@ 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()) diff --git a/test/test_api/test_documentation.py b/test/test_api/test_documentation.py index b86c68fc..4c09d612 100644 --- a/test/test_api/test_documentation.py +++ b/test/test_api/test_documentation.py @@ -37,6 +37,17 @@ 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' From 6cb58042271d180f6f92dec53271172c79ae063b Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 21:32:15 +0100 Subject: [PATCH 03/14] Revert "Upgrade Django" This reverts commit 195695edd348683a4ff955e7f892fe043d933858. --- jedi/third_party/django-stubs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jedi/third_party/django-stubs b/jedi/third_party/django-stubs index 28afe598..fd057010 160000 --- a/jedi/third_party/django-stubs +++ b/jedi/third_party/django-stubs @@ -1 +1 @@ -Subproject commit 28afe59881d796c483e10dde5169ee7cea5eef3d +Subproject commit fd057010f6cbf176f57d1099e82be46d39b99cb9 From 515e07227b37ffb88deeb6c9b099c7463d9612cb Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 21:44:29 +0100 Subject: [PATCH 04/14] Try to enable Python 3.10 in CI --- .github/workflows/ci.yml | 4 ++-- setup.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 188d3e34..ae5b13cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,8 +7,8 @@ jobs: strategy: matrix: os: [ubuntu-20.04, windows-2019] - python-version: [3.9, 3.8, 3.7, 3.6] - environment: ['3.8', '3.9', '3.7', '3.6', 'interpreter'] + python-version: [3.10, 3.9, 3.8, 3.7, 3.6] + environment: ['3.8', '3.10', '3.9', '3.7', '3.6', 'interpreter'] steps: - name: Checkout code uses: actions/checkout@v2 diff --git a/setup.py b/setup.py index 1f660a4d..099fe18c 100755 --- a/setup.py +++ b/setup.py @@ -61,6 +61,7 @@ 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', From 458bb30884a448198c171cdd2ac2e197b0d34462 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 21:46:00 +0100 Subject: [PATCH 05/14] Yaml got me again --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae5b13cf..e306eb50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04, windows-2019] - python-version: [3.10, 3.9, 3.8, 3.7, 3.6] + python-version: ["3.10", "3.9", "3.8", "3.7", "3.6"] environment: ['3.8', '3.10', '3.9', '3.7', '3.6', 'interpreter'] steps: - name: Checkout code From 8bd969c24aedbe9ce1c37ea1712a15a9b5553a75 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 21:51:03 +0100 Subject: [PATCH 06/14] Upgrade pytest --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 099fe18c..e20d14c2 100755 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ setup(name='jedi', install_requires=['parso>=0.8.0,<0.9.0'], extras_require={ 'testing': [ - 'pytest<6.0.0', + 'pytest<7.0.0', # docopt for sith doctests 'docopt', # coloroma for colored debug output From 42508d93096eee9949caa376d2af7a973fd6b234 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 22:57:25 +0100 Subject: [PATCH 07/14] Fix fixture annotations for pytest This means mostly these: @fixture def foo() -> Generator[int, None, None]: ... --- jedi/plugins/pytest.py | 10 +++++++++- test/completion/pytest.py | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/jedi/plugins/pytest.py b/jedi/plugins/pytest.py index 0e196c72..c78bdb4f 100644 --- a/jedi/plugins/pytest.py +++ b/jedi/plugins/pytest.py @@ -31,7 +31,15 @@ def execute(callback): def infer_anonymous_param(func): def get_returns(value): if value.tree_node.annotation is not None: - return value.execute_with_values() + 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 # In pytest we need to differentiate between generators and normal # returns. diff --git a/test/completion/pytest.py b/test/completion/pytest.py index 3c648c6c..a900dcda 100644 --- a/test/completion/pytest.py +++ b/test/completion/pytest.py @@ -1,3 +1,5 @@ +from typing import Generator + import pytest from pytest import fixture @@ -169,3 +171,15 @@ 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 From 6fa91726bf1c68a722bf0ea41e6422c2068ec072 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 23:08:05 +0100 Subject: [PATCH 08/14] Fix a test in Python 3.10 that's not really important anyway --- test/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_utils.py b/test/test_utils.py index c6198401..0dcf80db 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -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) < 15 + assert len(difference) < 30 def test_local_import(self): s = 'import test.test_utils' From b84604311749dd16ba8d270c22561465e884d849 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 23:19:21 +0100 Subject: [PATCH 09/14] Add 3.10 to the supported Python versions --- jedi/api/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jedi/api/environment.py b/jedi/api/environment.py index 3f1f238f..aea96c47 100644 --- a/jedi/api/environment.py +++ b/jedi/api/environment.py @@ -17,7 +17,7 @@ import parso _VersionInfo = namedtuple('VersionInfo', 'major minor micro') -_SUPPORTED_PYTHONS = ['3.9', '3.8', '3.7', '3.6'] +_SUPPORTED_PYTHONS = ['3.10', '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) From 656ecf502d24d03e3834a66b850ca1e16d4ba249 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 23:27:01 +0100 Subject: [PATCH 10/14] Prepare CHANGELOG for 0.18.1 --- CHANGELOG.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3cc23d43..52a6e792 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,11 @@ Changelog Unreleased ++++++++++ +0.18.1 (2021-11-17) ++++++++++++++++++++ + - Implict namespaces are now a separate types in ``Name().type`` +- Python 3.10 support 0.18.0 (2020-12-25) +++++++++++++++++++ From a17b95807847ea4d1830ece182ed30230129d51a Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 16 Nov 2021 23:36:22 +0100 Subject: [PATCH 11/14] Fix infer_default for params in REPL, fixes #1738 --- jedi/inference/names.py | 3 +++ test/test_api/test_interpreter.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/jedi/inference/names.py b/jedi/inference/names.py index 3e17ecd8..f446deb9 100644 --- a/jedi/inference/names.py +++ b/jedi/inference/names.py @@ -414,6 +414,9 @@ class ParamNameInterface(_ParamMixin): return 2 return 0 + def infer_default(self): + return NO_VALUES + class BaseTreeParamName(ParamNameInterface, AbstractTreeName): annotation_node = None diff --git a/test/test_api/test_interpreter.py b/test/test_api/test_interpreter.py index e6232e05..131ec6c1 100644 --- a/test/test_api/test_interpreter.py +++ b/test/test_api/test_interpreter.py @@ -732,3 +732,10 @@ def test_complete_not_findable_class_source(): 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() == [] From 8bc9c8cda2d5937e4922c2cfce0d9b4091dc5867 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Wed, 17 Nov 2021 00:14:59 +0100 Subject: [PATCH 12/14] Fix an issue where a slice is indexed, fixes #1748 --- jedi/inference/value/iterable.py | 22 ++++++++++++---------- test/completion/arrays.py | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/jedi/inference/value/iterable.py b/jedi/inference/value/iterable.py index 2f970fe8..7cc37173 100644 --- a/jedi/inference/value/iterable.py +++ b/jedi/inference/value/iterable.py @@ -342,6 +342,8 @@ 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): @@ -407,16 +409,6 @@ 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) @@ -472,6 +464,16 @@ 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) diff --git a/test/completion/arrays.py b/test/completion/arrays.py index 59b8f2fe..21437bce 100644 --- a/test/completion/arrays.py +++ b/test/completion/arrays.py @@ -44,6 +44,8 @@ b[int():] #? list() b[:] +#? int() +b[:, :-1] #? 3 b[:] @@ -67,6 +69,20 @@ 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 From 84d086a47b2ecd462ba6e231d2a155bc25e6b8a9 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Wed, 17 Nov 2021 00:31:40 +0100 Subject: [PATCH 13/14] Fix an issue with whitespace after a dot at the end of a file, also part of #1748 --- jedi/api/completion.py | 5 ++++- test/completion/docstring.py | 7 +++++++ test/test_api/test_completion.py | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/jedi/api/completion.py b/jedi/api/completion.py index a2ac4d04..342f7506 100644 --- a/jedi/api/completion.py +++ b/jedi/api/completion.py @@ -195,7 +195,6 @@ class Completion: - In args: */**: no completion - In params (also lambda): no completion before = """ - grammar = self._inference_state.grammar self.stack = stack = None self._position = ( @@ -278,6 +277,10 @@ 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(): diff --git a/test/completion/docstring.py b/test/completion/docstring.py index 5160610f..b2d02239 100644 --- a/test/completion/docstring.py +++ b/test/completion/docstring.py @@ -284,6 +284,13 @@ def doctest_with_space(): import_issu """ +def doctest_issue_github_1748(): + """From GitHub #1748 + #? 10 [] + This. Al + """ + pass + def docstring_rst_identifiers(): """ diff --git a/test/test_api/test_completion.py b/test/test_api/test_completion.py index 8e5ec3b2..de46223e 100644 --- a/test/test_api/test_completion.py +++ b/test/test_api/test_completion.py @@ -457,3 +457,7 @@ 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()] From ec9b453379f4492c8dfad8f006c353dea9c35466 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Wed, 17 Nov 2021 01:07:28 +0100 Subject: [PATCH 14/14] Handle defined_names for values that have no context, fixes #1744, fixes #1745 --- jedi/api/classes.py | 10 +++++++--- jedi/inference/base_value.py | 6 +++++- test/test_api/test_names.py | 6 ++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/jedi/api/classes.py b/jedi/api/classes.py index fba9b5ad..ee741c33 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -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 +from jedi.inference.base_value import ValueSet, HasNoContext from jedi.api.keywords import KeywordName from jedi.api import completion_cache from jedi.api.helpers import filter_follow_imports @@ -37,13 +37,17 @@ def _sort_names_by_start_pos(names): return sorted(names, key=lambda s: s.start_pos or (0, 0)) -def defined_names(inference_state, context): +def defined_names(inference_state, value): """ 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)] @@ -759,7 +763,7 @@ class Name(BaseName): """ defs = self._name.infer() return sorted( - unite(defined_names(self._inference_state, d.as_context()) for d in defs), + unite(defined_names(self._inference_state, d) for d in defs), key=lambda s: s._name.start_pos or (0, 0) ) diff --git a/jedi/inference/base_value.py b/jedi/inference/base_value.py index e51e063c..31b72937 100644 --- a/jedi/inference/base_value.py +++ b/jedi/inference/base_value.py @@ -22,6 +22,10 @@ from jedi.cache import memoize_method sentinel = object() +class HasNoContext(Exception): + pass + + class HelperValueMixin: def get_root_context(self): value = self @@ -261,7 +265,7 @@ class Value(HelperValueMixin): return self.parent_context.is_stub() def _as_context(self): - raise NotImplementedError('Not all values need to be converted to contexts: %s', self) + raise HasNoContext @property def name(self): diff --git a/test/test_api/test_names.py b/test/test_api/test_names.py index d43a29bb..287a301e 100644 --- a/test/test_api/test_names.py +++ b/test/test_api/test_names.py @@ -189,3 +189,9 @@ 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()