From 7d77f61040587212d1d6855c98e01ad8c3cdf220 Mon Sep 17 00:00:00 2001 From: Martin Vielsmaier Date: Tue, 31 Jan 2023 21:41:39 +0100 Subject: [PATCH 1/6] Add support for pytest fixtures from local pytest plugins. --- jedi/plugins/pytest.py | 14 ++++++++++++++ test/completion/conftest.py | 6 ++++++ test/completion/fixture_module.py | 6 ++++++ test/completion/pytest.py | 3 +++ 4 files changed, 29 insertions(+) create mode 100644 test/completion/fixture_module.py diff --git a/jedi/plugins/pytest.py b/jedi/plugins/pytest.py index 06adda44..e2fc6198 100644 --- a/jedi/plugins/pytest.py +++ b/jedi/plugins/pytest.py @@ -181,6 +181,9 @@ def _iter_pytest_modules(module_context, skip_own_module=False): if Path(file_io.path) != module_context.py__file__(): try: m = load_module_from_path(module_context.inference_state, file_io) + pytest_plugins_pointer = m.goto("pytest_plugins") + if pytest_plugins_pointer: + yield from _load_pytest_plugins(module_context, pytest_plugins_pointer[0]) yield m.as_context() except FileNotFoundError: pass @@ -196,6 +199,17 @@ def _iter_pytest_modules(module_context, skip_own_module=False): yield module_value.as_context() +def _load_pytest_plugins(module_context, plugins_pointers): + from jedi.inference.value import iterable + from parso.python.tree import String + for inferred in plugins_pointers.infer(): + if isinstance(inferred, iterable.SequenceLiteralValue): + for value in inferred.get_tree_entries(): + if isinstance(value, String): + for module_value in module_context.inference_state.import_module(eval(value.value).split(".")): + yield module_value.as_context() + + class FixtureFilter(ParserTreeFilter): def _filter(self, names): for name in super()._filter(names): diff --git a/test/completion/conftest.py b/test/completion/conftest.py index 7957be9a..1548d844 100644 --- a/test/completion/conftest.py +++ b/test/completion/conftest.py @@ -27,3 +27,9 @@ def capsysbinary(capsysbinary): #? ['close'] capsysbinary.clos return capsysbinary + + +# used when fixtures are defined in multiple files +pytest_plugins = [ + "completion.fixture_module", +] diff --git a/test/completion/fixture_module.py b/test/completion/fixture_module.py new file mode 100644 index 00000000..343c9281 --- /dev/null +++ b/test/completion/fixture_module.py @@ -0,0 +1,6 @@ +# Exists only for completion/pytest.py +import pytest + +@pytest.fixture +def my_module_fixture(): + return 1.0 diff --git a/test/completion/pytest.py b/test/completion/pytest.py index 64971dec..1f52c4f2 100644 --- a/test/completion/pytest.py +++ b/test/completion/pytest.py @@ -96,6 +96,9 @@ def test_x(my_con #? 18 ['my_conftest_fixture'] def test_x(my_conftest_fixture): return +#? ['my_module_fixture'] +def test_x(my_modu + return #? [] def lala(my_con From 7494c9495e516ae582d16401fc84e6d90726275c Mon Sep 17 00:00:00 2001 From: Martin Vielsmaier Date: Tue, 31 Jan 2023 21:43:06 +0100 Subject: [PATCH 2/6] Update contributors. --- AUTHORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index b86030e3..5171a933 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -63,6 +63,7 @@ Code Contributors - Leo Ryu (@Leo-Ryu) - Joseph Birkner (@josephbirkner) - Márcio Mazza (@marciomazza) +- Martin Vielsmaier (@moser) And a few more "anonymous" contributors. From 162034b387f4204c30852ed41a2cf71ecb9e225e Mon Sep 17 00:00:00 2001 From: Martin Vielsmaier Date: Tue, 31 Jan 2023 22:02:07 +0100 Subject: [PATCH 3/6] Fix line length. --- jedi/plugins/pytest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jedi/plugins/pytest.py b/jedi/plugins/pytest.py index e2fc6198..5bb27994 100644 --- a/jedi/plugins/pytest.py +++ b/jedi/plugins/pytest.py @@ -206,7 +206,8 @@ def _load_pytest_plugins(module_context, plugins_pointers): if isinstance(inferred, iterable.SequenceLiteralValue): for value in inferred.get_tree_entries(): if isinstance(value, String): - for module_value in module_context.inference_state.import_module(eval(value.value).split(".")): + names = eval(value.value).split(".") + for module_value in module_context.inference_state.import_module(names): yield module_value.as_context() From 9b8cece7efb7fbfb698749412eca9e0a6f1ead30 Mon Sep 17 00:00:00 2001 From: Martin Vielsmaier Date: Tue, 23 Jan 2024 20:50:11 +0100 Subject: [PATCH 4/6] Improve pytest plugin loading --- jedi/plugins/pytest.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/jedi/plugins/pytest.py b/jedi/plugins/pytest.py index 5bb27994..e1b64202 100644 --- a/jedi/plugins/pytest.py +++ b/jedi/plugins/pytest.py @@ -181,10 +181,12 @@ def _iter_pytest_modules(module_context, skip_own_module=False): if Path(file_io.path) != module_context.py__file__(): try: m = load_module_from_path(module_context.inference_state, file_io) - pytest_plugins_pointer = m.goto("pytest_plugins") - if pytest_plugins_pointer: - yield from _load_pytest_plugins(module_context, pytest_plugins_pointer[0]) - yield m.as_context() + conftest_module = m.as_context() + plugins_list = m.tree_node.get_used_names().get("pytest_plugins") + if plugins_list: + name = conftest_module.create_name(plugins_list[0]) + yield from _load_pytest_plugins(module_context, name) + yield conftest_module except FileNotFoundError: pass folder = folder.get_parent_folder() @@ -198,15 +200,15 @@ def _iter_pytest_modules(module_context, skip_own_module=False): for module_value in module_context.inference_state.import_module(names): yield module_value.as_context() +def _load_pytest_plugins(module_context, name): + from jedi.inference.helpers import get_str_or_none -def _load_pytest_plugins(module_context, plugins_pointers): - from jedi.inference.value import iterable - from parso.python.tree import String - for inferred in plugins_pointers.infer(): - if isinstance(inferred, iterable.SequenceLiteralValue): - for value in inferred.get_tree_entries(): - if isinstance(value, String): - names = eval(value.value).split(".") + for inferred in name.infer(): + for seq_value in inferred.py__iter__(): + for value in seq_value.infer(): + fq_name = get_str_or_none(value) + if fq_name: + names = fq_name.split(".") for module_value in module_context.inference_state.import_module(names): yield module_value.as_context() From 6982a499778422031c48f996bfacd2e56562360c Mon Sep 17 00:00:00 2001 From: Martin Vielsmaier Date: Wed, 24 Jan 2024 13:11:26 +0100 Subject: [PATCH 5/6] Fix codestyle --- jedi/plugins/pytest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jedi/plugins/pytest.py b/jedi/plugins/pytest.py index e1b64202..e2f8d5e0 100644 --- a/jedi/plugins/pytest.py +++ b/jedi/plugins/pytest.py @@ -200,6 +200,7 @@ def _iter_pytest_modules(module_context, skip_own_module=False): for module_value in module_context.inference_state.import_module(names): yield module_value.as_context() + def _load_pytest_plugins(module_context, name): from jedi.inference.helpers import get_str_or_none From 950ce70239d14d718844e7db5fd8edb720a19e66 Mon Sep 17 00:00:00 2001 From: Martin Vielsmaier Date: Fri, 26 Jan 2024 10:39:15 +0100 Subject: [PATCH 6/6] Prioritize conftest over plugins for pytest fixtures --- jedi/plugins/pytest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jedi/plugins/pytest.py b/jedi/plugins/pytest.py index e2f8d5e0..acf6ad38 100644 --- a/jedi/plugins/pytest.py +++ b/jedi/plugins/pytest.py @@ -182,11 +182,12 @@ def _iter_pytest_modules(module_context, skip_own_module=False): try: m = load_module_from_path(module_context.inference_state, file_io) conftest_module = m.as_context() + yield conftest_module + plugins_list = m.tree_node.get_used_names().get("pytest_plugins") if plugins_list: name = conftest_module.create_name(plugins_list[0]) yield from _load_pytest_plugins(module_context, name) - yield conftest_module except FileNotFoundError: pass folder = folder.get_parent_folder()