diff --git a/python_files/vscode_pytest/__init__.py b/python_files/vscode_pytest/__init__.py index e86b6e155ec3..a74142af1fa5 100644 --- a/python_files/vscode_pytest/__init__.py +++ b/python_files/vscode_pytest/__init__.py @@ -23,6 +23,12 @@ import pytest +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 from typing_extensions import NotRequired @@ -842,8 +848,23 @@ def create_test_node( Keyword arguments: test_case -- the pytest test case. """ + + 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 = ( - 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 +1282,4 @@ def pytest_plugin_registered(plugin: object, manager: pytest.PytestPluginManager and not manager.hasplugin(plugin_name) ): manager.register(DeferPlugin(), name=plugin_name) +