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..8fce53c1854a 100644 --- a/python_files/tests/unittestadapter/test_coverage.py +++ b/python_files/tests/unittestadapter/test_coverage.py @@ -53,11 +53,12 @@ def test_basic_coverage(): 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 +83,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..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)) @@ -75,8 +70,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 +85,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 +98,14 @@ 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: + 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: + 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)