Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Bugfixes
^^^^^^^^

* Fixed type hints of assert methods to match actual signature (`PR #1271 <https://github.com/pytest-dev/pytest-django/pull/1271>`__)
* Fixed ``--help``/``--version`` failing with ``AppRegistryNotReady`` (surfaced as a ``could not load initial conftests`` warning) when a ``conftest.py`` imports Django models at the top level. Django is now set up before the initial conftests are loaded for these options (`#1152 <https://github.com/pytest-dev/pytest-django/issues/1152>`__).
* Handled Django 6.2's ``ImproperlyConfigured`` (in addition to ``ImportError``) when the configured ``DJANGO_SETTINGS_MODULE`` cannot be imported, so pytest-django still shows its guidance message.

v4.12.0 (2026-02-14)
Expand Down
48 changes: 30 additions & 18 deletions pytest_django/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,13 @@ def pytest_load_initial_conftests(

options = parser.parse_known_args(args)

if options.version or options.help:
return
# With `--help`/`--version`, pytest still imports the initial conftests
# (it just doesn't run tests). We therefore keep setting Django up below,
# so conftests with top-level model imports don't fail with
# AppRegistryNotReady (issue #1152). A broken configuration is tolerated
# for these options, so they keep working regardless of Django's state
# (issue #235).
is_help_or_version = bool(options.version or options.help)

django_find_project = _get_boolean_value(
early_config.getini("django_find_project"), "django_find_project"
Expand Down Expand Up @@ -350,29 +355,36 @@ def _get_option_with_source(
report_header: list[str] = []
early_config.stash[report_header_key] = report_header

if ds:
report_header.append(f"settings: {ds} (from {ds_source})")
os.environ[SETTINGS_MODULE_ENV] = ds
try:
if ds:
report_header.append(f"settings: {ds} (from {ds_source})")
os.environ[SETTINGS_MODULE_ENV] = ds

if dc:
report_header.append(f"configuration: {dc} (from {dc_source})")
os.environ[CONFIGURATION_ENV] = dc
if dc:
report_header.append(f"configuration: {dc} (from {dc_source})")
os.environ[CONFIGURATION_ENV] = dc

# Install the django-configurations importer
import configurations.importer
# Install the django-configurations importer
import configurations.importer

configurations.importer.install()
configurations.importer.install()

# Forcefully load Django settings, throws ImportError or
# ImproperlyConfigured if settings cannot be loaded.
from django.conf import settings as dj_settings
# Forcefully load Django settings, throws ImportError or
# ImproperlyConfigured if settings cannot be loaded.
from django.conf import settings as dj_settings

with _handle_import_error(_django_project_scan_outcome):
dj_settings.DATABASES # noqa: B018
with _handle_import_error(_django_project_scan_outcome):
dj_settings.DATABASES # noqa: B018

early_config.stash[blocking_manager_key] = DjangoDbBlocker(_ispytest=True)
early_config.stash[blocking_manager_key] = DjangoDbBlocker(_ispytest=True)

_setup_django(early_config)
_setup_django(early_config)
except Exception:
Comment thread
alimony marked this conversation as resolved.
# `--help`/`--version` only print information and never run tests, so a
# broken Django configuration is tolerated here (issue #235); anything
# genuinely wrong still surfaces on a real test run.
if not is_help_or_version:
raise


@pytest.hookimpl(trylast=True)
Expand Down
39 changes: 39 additions & 0 deletions tests/test_initialization.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from textwrap import dedent

import pytest

from .helpers import DjangoPytester


Expand Down Expand Up @@ -60,3 +62,40 @@ def test_ds():
]
)
assert result.ret == 0


@pytest.mark.parametrize("option", ["--help", "--version"])
def test_django_setup_with_help_and_version(
django_pytester: DjangoPytester,
option: str,
) -> None:
"""Django must be set up before conftest files are imported, even for
``--help``/``--version``, so a top-level Django model import in a
``conftest.py`` does not fail with ``AppRegistryNotReady``.

Regression test for https://github.com/pytest-dev/pytest-django/issues/1152
"""
django_pytester.makeconftest(
"""
import sys

from tpkg.app.models import Item # noqa: F401

# Only reached if the model import above succeeds (i.e. Django is set
# up). With --version, pytest records but never prints the conftest
# load failure, so we assert on this positive marker instead.
print("conftest-imported-models", file=sys.stderr)
"""
)

# A single `--version`/`-V` is short-circuited before pytest loads any
# conftests (pytest #13574), so it wouldn't exercise this path; passing
# it twice forces full startup, which imports the initial conftests.
args = [option, option] if option == "--version" else [option]
result = django_pytester.runpytest_subprocess(*args)

output = result.stdout.str() + result.stderr.str()
assert result.ret == 0
assert "conftest-imported-models" in output
assert "AppRegistryNotReady" not in output
assert "could not load initial conftests" not in output