From 47f89ddd85b9fea01628af80fb6b5c18cf498e51 Mon Sep 17 00:00:00 2001 From: Wasim Date: Sat, 17 Oct 2026 17:46:37 +0530 Subject: [PATCH 1/4] fix(pytest): support parameterized tests with unbracketed names like pytest.HIDDEN_PARAM --- .../tests/pytestadapter/test_utils.py | 35 +++++++++++++++++++ python_files/vscode_pytest/__init__.py | 7 ++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/python_files/tests/pytestadapter/test_utils.py b/python_files/tests/pytestadapter/test_utils.py index 70201db7d097..e6264761519b 100644 --- a/python_files/tests/pytestadapter/test_utils.py +++ b/python_files/tests/pytestadapter/test_utils.py @@ -55,3 +55,38 @@ def test_cached_fsdecode(): result3 = cached_fsdecode(test_path2) assert result3 == os.fspath(test_path2) assert result3 != result1 + + +def test_hidden_param_discovery(tmp_path): + """Test that parameterized test with pytest.HIDDEN_PARAM builds tree without error.""" + import pytest + + test_file = tmp_path / "test_hidden.py" + test_file.write_text( + "import pytest\n" + "@pytest.mark.parametrize('setup', [pytest.param('setup', id=pytest.HIDDEN_PARAM)])\n" + "def test_main(setup):\n" + " assert True\n" + ) + + class Collector: + def __init__(self): + self.tree = None + + def pytest_sessionfinish(self, session, exitstatus): + from vscode_pytest import build_test_tree + + self.tree = build_test_tree(session) + + collector = Collector() + pytest.main(["--collect-only", str(test_file)], plugins=[collector]) + assert collector.tree is not None + file_nodes = list(collector.tree["children"].values()) + assert len(file_nodes) == 1 + file_node = file_nodes[0] + assert file_node["name"] == "test_hidden.py" + children = file_node["children"] + test_nodes = list(children.values() if hasattr(children, "values") else children) + assert len(test_nodes) == 1 + assert test_nodes[0]["name"] == "test_main" + assert test_nodes[0]["type_"] == "test" diff --git a/python_files/vscode_pytest/__init__.py b/python_files/vscode_pytest/__init__.py index 88f6327f6e8b..9fdc054d8ea8 100644 --- a/python_files/vscode_pytest/__init__.py +++ b/python_files/vscode_pytest/__init__.py @@ -647,6 +647,8 @@ def process_parameterized_test( """ function_name: str = "" # parameterized test cases cut the repetitive part of the name off. + if "[" not in test_node["name"]: + return test_node parent_part, parameterized_section = test_node["name"].split("[", 1) test_node["name"] = "[" + parameterized_section @@ -717,7 +719,8 @@ def build_test_tree(session: pytest.Session) -> TestNode: for test_case in session.items: test_node = create_test_node(test_case) - if hasattr(test_case, "callspec"): # This means it is a parameterized test. + is_parameterized = hasattr(test_case, "callspec") and "[" in test_node["name"] + if is_parameterized: # This means it is a parameterized test. # Process parameterized test and get the function node to use for further processing test_node = process_parameterized_test( test_case, test_node, function_nodes_dict, file_nodes_dict @@ -758,7 +761,7 @@ def build_test_tree(session: pytest.Session) -> TestNode: # Check if the class is already a child of the file node. if test_class_node is not None: test_file_node["children"].add(test_class_node) - elif not hasattr(test_case, "callspec"): + elif not is_parameterized: # This includes test cases that are pytest functions or a doctests. if test_case.parent is None: ERRORS.append(f"Test case {test_case.name} has no parent") From f7d6ad676a89a88a2d742767e3d3fd42eaf46710 Mon Sep 17 00:00:00 2001 From: Wasim Date: Sun, 18 Oct 2026 17:19:54 +0530 Subject: [PATCH 2/4] fix(pytest): resolve pyright type mismatch in process_parameterized_test --- python_files/vscode_pytest/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python_files/vscode_pytest/__init__.py b/python_files/vscode_pytest/__init__.py index 9fdc054d8ea8..0446aec1e10f 100644 --- a/python_files/vscode_pytest/__init__.py +++ b/python_files/vscode_pytest/__init__.py @@ -647,8 +647,6 @@ def process_parameterized_test( """ function_name: str = "" # parameterized test cases cut the repetitive part of the name off. - if "[" not in test_node["name"]: - return test_node parent_part, parameterized_section = test_node["name"].split("[", 1) test_node["name"] = "[" + parameterized_section From 9f10dad688de9f6ed6b65ac98cba9c7d4a2705a7 Mon Sep 17 00:00:00 2001 From: Wasim Date: Sun, 18 Oct 2026 17:28:25 +0530 Subject: [PATCH 3/4] style: add noqa ARG002 for pytest hook parameter --- python_files/tests/pytestadapter/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_files/tests/pytestadapter/test_utils.py b/python_files/tests/pytestadapter/test_utils.py index e6264761519b..ef5e3c2af946 100644 --- a/python_files/tests/pytestadapter/test_utils.py +++ b/python_files/tests/pytestadapter/test_utils.py @@ -73,7 +73,7 @@ class Collector: def __init__(self): self.tree = None - def pytest_sessionfinish(self, session, exitstatus): + def pytest_sessionfinish(self, session, exitstatus): # noqa: ARG002 from vscode_pytest import build_test_tree self.tree = build_test_tree(session) From 7ea63884d3c8416092fd0aa2ce0136cf1f536e17 Mon Sep 17 00:00:00 2001 From: Wasim Date: Sun, 18 Oct 2026 17:40:39 +0530 Subject: [PATCH 4/4] test(pytest): isolate rootdir to tmp_path for cross-platform drive safety --- python_files/tests/pytestadapter/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_files/tests/pytestadapter/test_utils.py b/python_files/tests/pytestadapter/test_utils.py index ef5e3c2af946..939430e833d2 100644 --- a/python_files/tests/pytestadapter/test_utils.py +++ b/python_files/tests/pytestadapter/test_utils.py @@ -79,7 +79,7 @@ def pytest_sessionfinish(self, session, exitstatus): # noqa: ARG002 self.tree = build_test_tree(session) collector = Collector() - pytest.main(["--collect-only", str(test_file)], plugins=[collector]) + pytest.main(["--collect-only", f"--rootdir={tmp_path}", str(test_file)], plugins=[collector]) assert collector.tree is not None file_nodes = list(collector.tree["children"].values()) assert len(file_nodes) == 1