From 0b688251c09f462a07caa8b42baddfb30d688304 Mon Sep 17 00:00:00 2001 From: Danila Grobov Date: Fri, 4 Apr 2025 23:45:14 +0300 Subject: [PATCH 1/3] Adapt the django coverage fix to work with older django versions. --- .../.data/simple_django/old_manage.py | 21 +++++++++++++++ .../tests/unittestadapter/test_coverage.py | 8 +++--- .../unittestadapter/django_handler.py | 27 ++++++++++--------- 3 files changed, 39 insertions(+), 17 deletions(-) create mode 100755 python_files/tests/unittestadapter/.data/simple_django/old_manage.py diff --git a/python_files/tests/unittestadapter/.data/simple_django/old_manage.py b/python_files/tests/unittestadapter/.data/simple_django/old_manage.py new file mode 100755 index 000000000000..844b98b4edba --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/old_manage.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +import os +import sys +if __name__ == "__main__": + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') + try: + from django.core.management import execute_from_command_line + except ImportError: + # The above import may fail for some other reason. Ensure that the + # issue is really that Django is missing to avoid masking other + # exceptions on Python 2. + try: + import django + except ImportError: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) + raise + execute_from_command_line(sys.argv) diff --git a/python_files/tests/unittestadapter/test_coverage.py b/python_files/tests/unittestadapter/test_coverage.py index d357d95ad111..3a1b058d6426 100644 --- a/python_files/tests/unittestadapter/test_coverage.py +++ b/python_files/tests/unittestadapter/test_coverage.py @@ -52,12 +52,12 @@ def test_basic_coverage(): assert set(focal_function_coverage.get("lines_covered")) == {4, 5, 7, 9, 10, 11, 12, 13, 14} assert set(focal_function_coverage.get("lines_missed")) == {6} - +@pytest.mark.parametrize("manage_py_file", ["manage.py", "old_manage.py"]) @pytest.mark.timeout(30) -def test_basic_django_coverage(): +def test_basic_django_coverage(manage_py_file): """This test validates that the coverage is correctly calculated for a Django project.""" data_path: pathlib.Path = TEST_DATA_PATH / "simple_django" - manage_py_path: str = os.fsdecode(data_path / "manage.py") + manage_py_path: str = os.fsdecode(data_path / manage_py_file) execution_script: pathlib.Path = python_files_path / "unittestadapter" / "execution.py" test_ids = [ @@ -82,7 +82,7 @@ def test_basic_django_coverage(): assert coverage results = coverage["result"] assert results - assert len(results) == 15 + assert len(results) == 16 polls_views_coverage = results.get(str(data_path / "polls" / "views.py")) assert polls_views_coverage assert polls_views_coverage.get("lines_covered") is not None diff --git a/python_files/unittestadapter/django_handler.py b/python_files/unittestadapter/django_handler.py index 4230c951e162..19327c385ca0 100644 --- a/python_files/unittestadapter/django_handler.py +++ b/python_files/unittestadapter/django_handler.py @@ -75,8 +75,9 @@ def django_discovery_runner(manage_py_path: str, args: List[str]) -> None: def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List[str]) -> None: + manage_path: pathlib.Path = pathlib.Path(manage_py_path) # Attempt a small amount of validation on the manage.py path. - if not pathlib.Path(manage_py_path).exists(): + if not manage_path.exists(): raise VSCodeUnittestError("Error running Django, manage.py path does not exist.") try: @@ -89,20 +90,12 @@ def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List else: env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) - django_project_dir: pathlib.Path = pathlib.Path(manage_py_path).parent + django_project_dir: pathlib.Path = manage_path.parent sys.path.insert(0, os.fspath(django_project_dir)) print(f"Django project directory: {django_project_dir}") - manage_spec: ModuleSpec | None = importlib.util.spec_from_file_location( - "manage", manage_py_path - ) - if manage_spec is None or manage_spec.loader is None: - raise VSCodeUnittestError("Error importing manage.py when running Django testing.") - manage_module = importlib.util.module_from_spec(manage_spec) - manage_spec.loader.exec_module(manage_module) - manage_argv: List[str] = [ - manage_py_path, + str(manage_path), "test", "--testrunner=django_test_runner.CustomExecutionTestRunner", *args, @@ -110,7 +103,15 @@ def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List ] print(f"Django manage.py arguments: {manage_argv}") - with override_argv(manage_argv), suppress(SystemExit): - manage_module.main() + try: + with ( + override_argv(manage_argv), + suppress(SystemExit), + manage_path.open() as manage_file, + ): + manage_code = manage_file.read() + exec(manage_code, {"__name__": "__main__"}) + except OSError as e: + raise VSCodeUnittestError("Error running Django, unable to read manage.py") from e except Exception as e: print(f"Error during Django test execution: {e}", file=sys.stderr) From e0510597ffd5faef94a6c2bf281df7aa98a70c14 Mon Sep 17 00:00:00 2001 From: Danila Grobov Date: Sat, 5 Apr 2025 00:24:16 +0300 Subject: [PATCH 2/3] FIx python 3.8 and linter issues. --- python_files/unittestadapter/django_handler.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/python_files/unittestadapter/django_handler.py b/python_files/unittestadapter/django_handler.py index 19327c385ca0..77c50efc27d0 100644 --- a/python_files/unittestadapter/django_handler.py +++ b/python_files/unittestadapter/django_handler.py @@ -1,17 +1,12 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -import importlib.util import os import pathlib import subprocess import sys from contextlib import contextmanager, suppress -from typing import TYPE_CHECKING, Generator, List - -if TYPE_CHECKING: - from importlib.machinery import ModuleSpec - +from typing import Generator, List script_dir = pathlib.Path(__file__).parent sys.path.append(os.fspath(script_dir)) @@ -104,11 +99,10 @@ def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List print(f"Django manage.py arguments: {manage_argv}") try: - with ( - override_argv(manage_argv), - suppress(SystemExit), - manage_path.open() as manage_file, - ): + argv_context = override_argv(manage_argv) + suppress_context = suppress(SystemExit) + manage_file = manage_path.open() + with argv_context, suppress_context, manage_file: manage_code = manage_file.read() exec(manage_code, {"__name__": "__main__"}) except OSError as e: From e67ba5b5f4c1d2ea37ca9ab3bceaec6efda73fa6 Mon Sep 17 00:00:00 2001 From: Danila Grobov Date: Sat, 5 Apr 2025 01:03:43 +0300 Subject: [PATCH 3/3] Fix formatting. --- python_files/tests/unittestadapter/test_coverage.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python_files/tests/unittestadapter/test_coverage.py b/python_files/tests/unittestadapter/test_coverage.py index 3a1b058d6426..8fce53c1854a 100644 --- a/python_files/tests/unittestadapter/test_coverage.py +++ b/python_files/tests/unittestadapter/test_coverage.py @@ -52,6 +52,7 @@ def test_basic_coverage(): assert set(focal_function_coverage.get("lines_covered")) == {4, 5, 7, 9, 10, 11, 12, 13, 14} assert set(focal_function_coverage.get("lines_missed")) == {6} + @pytest.mark.parametrize("manage_py_file", ["manage.py", "old_manage.py"]) @pytest.mark.timeout(30) def test_basic_django_coverage(manage_py_file):