diff options
| author | Jacob Walls <jacobtylerwalls@gmail.com> | 2026-01-14 08:25:37 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-14 08:25:37 -0500 |
| commit | 6a596373d464d1d8e9014d952286ab5f6d2a9983 (patch) | |
| tree | e08e4ab29c97a6612a6d2c8c6c334add51047ea5 | |
| parent | 040bb3eba72eb45020dd025d3f83094a0fcaf22f (diff) | |
Fixed #35402 -- Fixed crash in DatabaseFeatures.django_test_skips when running a subset of tests.
Thanks Tim Graham for the report and the review.
| -rw-r--r-- | django/db/backends/base/creation.py | 48 | ||||
| -rw-r--r-- | tests/backends/base/test_creation.py | 5 |
2 files changed, 35 insertions, 18 deletions
diff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py index ed2b0738a5..5087f80b7e 100644 --- a/django/db/backends/base/creation.py +++ b/django/db/backends/base/creation.py @@ -358,27 +358,39 @@ class BaseDatabaseCreation: Mark tests in Django's test suite which are expected failures on this database and test which should be skipped on this database. """ - # Only load unittest if we're actually testing. - from unittest import expectedFailure, skip - for test_name in self.connection.features.django_test_expected_failures: - test_case_name, _, test_method_name = test_name.rpartition(".") - test_app = test_name.split(".")[0] - # Importing a test app that isn't installed raises RuntimeError. - if test_app in settings.INSTALLED_APPS: - test_case = import_string(test_case_name) - test_method = getattr(test_case, test_method_name) - setattr(test_case, test_method_name, expectedFailure(test_method)) + self._mark_test(test_name) for reason, tests in self.connection.features.django_test_skips.items(): for test_name in tests: - test_case_name, _, test_method_name = test_name.rpartition(".") - test_app = test_name.split(".")[0] - # Importing a test app that isn't installed raises - # RuntimeError. - if test_app in settings.INSTALLED_APPS: - test_case = import_string(test_case_name) - test_method = getattr(test_case, test_method_name) - setattr(test_case, test_method_name, skip(reason)(test_method)) + self._mark_test(test_name, reason) + + def _mark_test(self, test_name, skip_reason=None): + # Only load unittest during testing. + from unittest import expectedFailure, skip + + module_or_class_name, _, name_to_mark = test_name.rpartition(".") + test_app = test_name.split(".")[0] + # Importing a test app that isn't installed raises RuntimeError. + if test_app in settings.INSTALLED_APPS: + try: + test_frame = import_string(module_or_class_name) + except ImportError: + # import_string() can raise ImportError if a submodule's parent + # module hasn't already been imported during test discovery. + # This can happen in at least two cases: + # 1. When running a subset of tests in a module, the test + # runner won't import tests in that module's other + # submodules. + # 2. When the parallel test runner spawns workers with an empty + # import cache. + test_to_mark = import_string(test_name) + test_frame = sys.modules.get(test_to_mark.__module__) + else: + test_to_mark = getattr(test_frame, name_to_mark) + if skip_reason: + setattr(test_frame, name_to_mark, skip(skip_reason)(test_to_mark)) + else: + setattr(test_frame, name_to_mark, expectedFailure(test_to_mark)) def sql_table_creation_suffix(self): """ diff --git a/tests/backends/base/test_creation.py b/tests/backends/base/test_creation.py index 2542407f0b..8fd8f490b2 100644 --- a/tests/backends/base/test_creation.py +++ b/tests/backends/base/test_creation.py @@ -1,6 +1,7 @@ import copy import datetime import os +import sys from unittest import mock from django.db import DEFAULT_DB_ALIAS, connection, connections @@ -333,6 +334,10 @@ class TestMarkTests(SimpleTestCase): "backends.base.test_creation.skip_test_function", }, } + # Emulate the scenario where the parent module for + # backends.base.test_creation has not been imported yet. + popped_module = sys.modules.pop("backends.base") + self.addCleanup(sys.modules.__setitem__, "backends.base", popped_module) creation.mark_expected_failures_and_skips() self.assertIs( expected_failure_test_function.__unittest_expecting_failure__, |
