Skip to content

Commit bb6e909

Browse files
Test Coverage for older django versions (#24968)
Related to this issue: #24199 @mcobalchinisoftfocus Discovered an issue with older django versions, which didn't have the main function in the manage.py #24927 (comment) I've fixed this issue by executing the code in manage.py with __name__ set to __main__ instead of relying on main function being there. I've also adjusted the test, so that it would cover this case.
1 parent e9fb2bf commit bb6e909

File tree

3 files changed

+39
-22
lines changed

3 files changed

+39
-22
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env python
2+
import os
3+
import sys
4+
if __name__ == "__main__":
5+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
6+
try:
7+
from django.core.management import execute_from_command_line
8+
except ImportError:
9+
# The above import may fail for some other reason. Ensure that the
10+
# issue is really that Django is missing to avoid masking other
11+
# exceptions on Python 2.
12+
try:
13+
import django
14+
except ImportError:
15+
raise ImportError(
16+
"Couldn't import Django. Are you sure it's installed and "
17+
"available on your PYTHONPATH environment variable? Did you "
18+
"forget to activate a virtual environment?"
19+
)
20+
raise
21+
execute_from_command_line(sys.argv)

python_files/tests/unittestadapter/test_coverage.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,12 @@ def test_basic_coverage():
5353
assert set(focal_function_coverage.get("lines_missed")) == {6}
5454

5555

56+
@pytest.mark.parametrize("manage_py_file", ["manage.py", "old_manage.py"])
5657
@pytest.mark.timeout(30)
57-
def test_basic_django_coverage():
58+
def test_basic_django_coverage(manage_py_file):
5859
"""This test validates that the coverage is correctly calculated for a Django project."""
5960
data_path: pathlib.Path = TEST_DATA_PATH / "simple_django"
60-
manage_py_path: str = os.fsdecode(data_path / "manage.py")
61+
manage_py_path: str = os.fsdecode(data_path / manage_py_file)
6162
execution_script: pathlib.Path = python_files_path / "unittestadapter" / "execution.py"
6263

6364
test_ids = [
@@ -82,7 +83,7 @@ def test_basic_django_coverage():
8283
assert coverage
8384
results = coverage["result"]
8485
assert results
85-
assert len(results) == 15
86+
assert len(results) == 16
8687
polls_views_coverage = results.get(str(data_path / "polls" / "views.py"))
8788
assert polls_views_coverage
8889
assert polls_views_coverage.get("lines_covered") is not None

python_files/unittestadapter/django_handler.py

+14-19
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33

4-
import importlib.util
54
import os
65
import pathlib
76
import subprocess
87
import sys
98
from contextlib import contextmanager, suppress
10-
from typing import TYPE_CHECKING, Generator, List
11-
12-
if TYPE_CHECKING:
13-
from importlib.machinery import ModuleSpec
14-
9+
from typing import Generator, List
1510

1611
script_dir = pathlib.Path(__file__).parent
1712
sys.path.append(os.fspath(script_dir))
@@ -75,8 +70,9 @@ def django_discovery_runner(manage_py_path: str, args: List[str]) -> None:
7570

7671

7772
def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List[str]) -> None:
73+
manage_path: pathlib.Path = pathlib.Path(manage_py_path)
7874
# Attempt a small amount of validation on the manage.py path.
79-
if not pathlib.Path(manage_py_path).exists():
75+
if not manage_path.exists():
8076
raise VSCodeUnittestError("Error running Django, manage.py path does not exist.")
8177

8278
try:
@@ -89,28 +85,27 @@ def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List
8985
else:
9086
env["PYTHONPATH"] = os.fspath(custom_test_runner_dir)
9187

92-
django_project_dir: pathlib.Path = pathlib.Path(manage_py_path).parent
88+
django_project_dir: pathlib.Path = manage_path.parent
9389
sys.path.insert(0, os.fspath(django_project_dir))
9490
print(f"Django project directory: {django_project_dir}")
9591

96-
manage_spec: ModuleSpec | None = importlib.util.spec_from_file_location(
97-
"manage", manage_py_path
98-
)
99-
if manage_spec is None or manage_spec.loader is None:
100-
raise VSCodeUnittestError("Error importing manage.py when running Django testing.")
101-
manage_module = importlib.util.module_from_spec(manage_spec)
102-
manage_spec.loader.exec_module(manage_module)
103-
10492
manage_argv: List[str] = [
105-
manage_py_path,
93+
str(manage_path),
10694
"test",
10795
"--testrunner=django_test_runner.CustomExecutionTestRunner",
10896
*args,
10997
*test_ids,
11098
]
11199
print(f"Django manage.py arguments: {manage_argv}")
112100

113-
with override_argv(manage_argv), suppress(SystemExit):
114-
manage_module.main()
101+
try:
102+
argv_context = override_argv(manage_argv)
103+
suppress_context = suppress(SystemExit)
104+
manage_file = manage_path.open()
105+
with argv_context, suppress_context, manage_file:
106+
manage_code = manage_file.read()
107+
exec(manage_code, {"__name__": "__main__"})
108+
except OSError as e:
109+
raise VSCodeUnittestError("Error running Django, unable to read manage.py") from e
115110
except Exception as e:
116111
print(f"Error during Django test execution: {e}", file=sys.stderr)

0 commit comments

Comments
 (0)