|
57 | 57 | from typing import Any, NoReturn |
58 | 58 |
|
59 | 59 | import django |
| 60 | + import django.apps.registry |
60 | 61 |
|
61 | 62 |
|
62 | 63 | SETTINGS_MODULE_ENV = "DJANGO_SETTINGS_MODULE" |
@@ -294,6 +295,10 @@ def pytest_load_initial_conftests( |
294 | 295 | "a string specifying the module of a URL config, e.g. " |
295 | 296 | '"my_app.test_urls".', |
296 | 297 | ) |
| 298 | + early_config.addinivalue_line( |
| 299 | + "markers", |
| 300 | + "django_isolate_apps(*app_labels): isolate Django's app registry for this test.", |
| 301 | + ) |
297 | 302 | early_config.addinivalue_line( |
298 | 303 | "markers", |
299 | 304 | "ignore_template_errors(): ignore errors from invalid template " |
@@ -669,6 +674,39 @@ def _django_set_urlconf(request: pytest.FixtureRequest) -> Generator[None, None, |
669 | 674 | set_urlconf(None) |
670 | 675 |
|
671 | 676 |
|
| 677 | +@pytest.fixture(autouse=True) |
| 678 | +def _django_isolate_apps( |
| 679 | + request: pytest.FixtureRequest, |
| 680 | +) -> Generator[django.apps.registry.Apps, None, None]: |
| 681 | + """Apply the @pytest.mark.django_isolate_apps marker if present, internal to pytest-django.""" |
| 682 | + marker: pytest.Mark | None = request.node.get_closest_marker("django_isolate_apps") |
| 683 | + if not marker: |
| 684 | + yield None |
| 685 | + return |
| 686 | + |
| 687 | + skip_if_no_django() |
| 688 | + |
| 689 | + from django.test.utils import isolate_apps |
| 690 | + |
| 691 | + app_labels = validate_django_isolate_apps(marker) |
| 692 | + |
| 693 | + with isolate_apps(*app_labels) as apps: |
| 694 | + yield apps |
| 695 | + |
| 696 | + |
| 697 | +@pytest.fixture |
| 698 | +def django_isolated_apps( |
| 699 | + _django_isolate_apps: django.apps.registry.Apps | None, |
| 700 | +) -> django.apps.registry.Apps: |
| 701 | + """Access the isolated Apps registry instance for tests marked with |
| 702 | + @pytest.mark.django_isolate_apps(...).""" |
| 703 | + if _django_isolate_apps is None: |
| 704 | + raise pytest.UsageError( |
| 705 | + "The django_isolated_apps fixture requires @pytest.mark.django_isolate_apps([...])." |
| 706 | + ) |
| 707 | + return _django_isolate_apps |
| 708 | + |
| 709 | + |
672 | 710 | @pytest.fixture(autouse=True, scope="session") |
673 | 711 | def _fail_for_invalid_template_variable() -> Generator[None, None, None]: |
674 | 712 | """Fixture that fails for invalid variables in templates. |
@@ -887,3 +925,14 @@ def apifun(urls: list[str]) -> list[str]: |
887 | 925 | return urls |
888 | 926 |
|
889 | 927 | return apifun(*marker.args, **marker.kwargs) |
| 928 | + |
| 929 | + |
| 930 | +def validate_django_isolate_apps(marker: pytest.Mark) -> tuple[str, ...]: |
| 931 | + """Validate the django_isolate_apps marker.""" |
| 932 | + |
| 933 | + def apifun(*app_labels: str) -> tuple[str, ...]: |
| 934 | + if not app_labels: |
| 935 | + raise ValueError("@pytest.mark.django_isolate_apps requires at least one app label") |
| 936 | + return app_labels |
| 937 | + |
| 938 | + return apifun(*marker.args, **marker.kwargs) |
0 commit comments