From 7e2d97ebbd43b955a8af5303d1dc940d7f6eb1ac Mon Sep 17 00:00:00 2001 From: Vaclav Hala Date: Mon, 22 Jun 2026 15:56:23 +0200 Subject: [PATCH 1/3] try getting pytest case lineno by internal API to avoid unnecessary expensive call before falling back to location query --- python_files/vscode_pytest/__init__.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/python_files/vscode_pytest/__init__.py b/python_files/vscode_pytest/__init__.py index e86b6e155ec3..ea5b163b3f1a 100644 --- a/python_files/vscode_pytest/__init__.py +++ b/python_files/vscode_pytest/__init__.py @@ -22,6 +22,8 @@ ) import pytest +from _pytest.python import PyobjMixin +from _pytest._code.code import Code if TYPE_CHECKING: from pluggy import Result @@ -842,8 +844,18 @@ def create_test_node( Keyword arguments: test_case -- the pytest test case. """ + + try: + # test_case.location returns tuple of path and line number + # Obtaining the path is expensive and we do not need it anyway. + # Before falling back to that high-level API, we try to use + # internal API to get just line number without the path. + lineno0 = Code.from_function(cast(PyobjMixin, test_case).obj).firstlineno + except: + lineno0 = test_case.location[1] + test_case_loc: str = ( - str(test_case.location[1] + 1) if (test_case.location[1] is not None) else "" + str(lineno0 + 1) if (lineno0 is not None) else "" ) absolute_test_id = get_absolute_test_id(test_case.nodeid, get_node_path(test_case)) return { @@ -1261,3 +1273,4 @@ def pytest_plugin_registered(plugin: object, manager: pytest.PytestPluginManager and not manager.hasplugin(plugin_name) ): manager.register(DeferPlugin(), name=plugin_name) + From 0923fbfc6e186bf9ac6033b3636ac54b71ee1c04 Mon Sep 17 00:00:00 2001 From: Vaclav Hala Date: Mon, 6 Jul 2026 13:57:47 +0200 Subject: [PATCH 2/3] guard agains changes in _pytest internals --- python_files/vscode_pytest/__init__.py | 27 +++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/python_files/vscode_pytest/__init__.py b/python_files/vscode_pytest/__init__.py index ea5b163b3f1a..a74142af1fa5 100644 --- a/python_files/vscode_pytest/__init__.py +++ b/python_files/vscode_pytest/__init__.py @@ -22,8 +22,12 @@ ) import pytest -from _pytest.python import PyobjMixin -from _pytest._code.code import Code + +PYTEST_INTERNAL_AVAILABLE = False +with contextlib.suppress(ImportError): + from _pytest.python import PyobjMixin + from _pytest._code.code import Code + PYTEST_INTERNAL_AVAILABLE = True if TYPE_CHECKING: from pluggy import Result @@ -845,13 +849,18 @@ def create_test_node( test_case -- the pytest test case. """ - try: - # test_case.location returns tuple of path and line number - # Obtaining the path is expensive and we do not need it anyway. - # Before falling back to that high-level API, we try to use - # internal API to get just line number without the path. - lineno0 = Code.from_function(cast(PyobjMixin, test_case).obj).firstlineno - except: + lineno0 = None + if PYTEST_INTERNAL_AVAILABLE: + try: + # test_case.location returns tuple of path and line number + # Obtaining the path is expensive and we do not need it anyway. + # Before falling back to that high-level API, we try to use + # internal API to get just line number without the path. + lineno0 = Code.from_function(cast(PyobjMixin, test_case).obj).firstlineno # type: ignore + except Exception: + pass + + if lineno0 is None: lineno0 = test_case.location[1] test_case_loc: str = ( From e348e2fc90b1ca0ac3a378a81a5292adb87fd1c5 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Fri, 24 Jul 2026 10:55:17 -0700 Subject: [PATCH 3/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- python_files/vscode_pytest/__init__.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/python_files/vscode_pytest/__init__.py b/python_files/vscode_pytest/__init__.py index a74142af1fa5..886bf516b9e2 100644 --- a/python_files/vscode_pytest/__init__.py +++ b/python_files/vscode_pytest/__init__.py @@ -849,20 +849,17 @@ def create_test_node( test_case -- the pytest test case. """ - lineno0 = None + lineno0: int | None = None if PYTEST_INTERNAL_AVAILABLE: - try: - # test_case.location returns tuple of path and line number - # Obtaining the path is expensive and we do not need it anyway. - # Before falling back to that high-level API, we try to use - # internal API to get just line number without the path. - lineno0 = Code.from_function(cast(PyobjMixin, test_case).obj).firstlineno # type: ignore - except Exception: - pass - + obj = getattr(test_case, "obj", None) + if obj is not None: + try: + lineno0 = Code.from_function(obj).firstlineno + except (AttributeError, OSError, TypeError): + lineno0 = None + if lineno0 is None: lineno0 = test_case.location[1] - test_case_loc: str = ( str(lineno0 + 1) if (lineno0 is not None) else "" )