Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require pytest>=7.0 #1078

Merged
merged 3 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pytest-django allows you to test your Django project/applications with the
* Django: 3.2, 4.0, 4.1, 4.2 and latest main branch (compatible at the time of
each release)
* Python: CPython>=3.8 or PyPy 3
* pytest: >=5.4
* pytest: >=7.0

For compatibility with older versions, use the pytest-django 3.*.* series.

Expand Down
7 changes: 3 additions & 4 deletions pytest_django/asserts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
Dynamically load all Django assertion cases and expose them for importing.
"""
from functools import wraps
from typing import Any, Callable, Optional, Sequence, Set, Type, Union
from typing import (
TYPE_CHECKING, Any, Callable, Optional, Sequence, Set, Type, Union,
)

from django.test import (
LiveServerTestCase, SimpleTestCase, TestCase, TransactionTestCase,
)


TYPE_CHECKING = False


test_case = TestCase("run")


Expand Down
41 changes: 21 additions & 20 deletions pytest_django/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
import os
from contextlib import contextmanager
from functools import partial
from typing import Any, Generator, Iterable, List, Optional, Tuple, Union
from typing import (
TYPE_CHECKING, Any, Generator, Iterable, List, Literal, Optional, Tuple,
Union,
)

import pytest

Expand All @@ -11,16 +14,14 @@
from .lazy_django import skip_if_no_django


TYPE_CHECKING = False
if TYPE_CHECKING:
from typing import Literal

import django

_DjangoDbDatabases = Optional[Union["Literal['__all__']", Iterable[str]]]
_DjangoDbAvailableApps = Optional[List[str]]
# transaction, reset_sequences, databases, serialized_rollback, available_apps
_DjangoDb = Tuple[bool, bool, _DjangoDbDatabases, bool, _DjangoDbAvailableApps]

_DjangoDbDatabases = Optional[Union[Literal['__all__'], Iterable[str]]]
_DjangoDbAvailableApps = Optional[List[str]]
# transaction, reset_sequences, databases, serialized_rollback, available_apps
_DjangoDb = Tuple[bool, bool, _DjangoDbDatabases, bool, _DjangoDbAvailableApps]


__all__ = [
Expand Down Expand Up @@ -57,7 +58,7 @@ def django_db_modify_db_settings_tox_suffix() -> None:


@pytest.fixture(scope="session")
def django_db_modify_db_settings_xdist_suffix(request) -> None:
def django_db_modify_db_settings_xdist_suffix(request: pytest.FixtureRequest) -> None:
skip_if_no_django()

xdist_suffix = getattr(request.config, "workerinput", {}).get("workerid")
Expand All @@ -82,23 +83,23 @@ def django_db_modify_db_settings(


@pytest.fixture(scope="session")
def django_db_use_migrations(request) -> bool:
def django_db_use_migrations(request: pytest.FixtureRequest) -> bool:
return not request.config.getvalue("nomigrations")


@pytest.fixture(scope="session")
def django_db_keepdb(request) -> bool:
def django_db_keepdb(request: pytest.FixtureRequest) -> bool:
return request.config.getvalue("reuse_db")


@pytest.fixture(scope="session")
def django_db_createdb(request) -> bool:
def django_db_createdb(request: pytest.FixtureRequest) -> bool:
return request.config.getvalue("create_db")


@pytest.fixture(scope="session")
def django_db_setup(
request,
request: pytest.FixtureRequest,
django_test_environment: None,
django_db_blocker,
django_db_use_migrations: bool,
Expand Down Expand Up @@ -141,7 +142,7 @@ def teardown_database() -> None:

@pytest.fixture()
def _django_db_helper(
request,
request: pytest.FixtureRequest,
django_db_setup: None,
django_db_blocker,
) -> None:
Expand Down Expand Up @@ -239,7 +240,7 @@ def tearDownClass(cls) -> None:
request.addfinalizer(test_case._post_teardown)


def validate_django_db(marker) -> "_DjangoDb":
def validate_django_db(marker) -> _DjangoDb:
"""Validate the django_db marker.

It checks the signature and creates the ``transaction``,
Expand All @@ -254,10 +255,10 @@ def validate_django_db(marker) -> "_DjangoDb":
def apifun(
transaction: bool = False,
reset_sequences: bool = False,
databases: "_DjangoDbDatabases" = None,
databases: _DjangoDbDatabases = None,
serialized_rollback: bool = False,
available_apps: "_DjangoDbAvailableApps" = None,
) -> "_DjangoDb":
available_apps: _DjangoDbAvailableApps = None,
) -> _DjangoDb:
return transaction, reset_sequences, databases, serialized_rollback, available_apps

return apifun(*marker.args, **marker.kwargs)
Expand Down Expand Up @@ -517,7 +518,7 @@ def settings():


@pytest.fixture(scope="session")
def live_server(request):
def live_server(request: pytest.FixtureRequest):
"""Run a live Django server in the background during tests

The address the server is started from is taken from the
Expand Down Expand Up @@ -548,7 +549,7 @@ def live_server(request):


@pytest.fixture(autouse=True, scope="function")
def _live_server_helper(request) -> None:
def _live_server_helper(request: pytest.FixtureRequest) -> None:
"""Helper to make live_server work, internal to pytest-django.

This helper will dynamically request the transactional_db fixture
Expand Down
88 changes: 45 additions & 43 deletions pytest_django/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
import pathlib
import sys
from functools import reduce
from typing import Generator, List, Optional, Tuple, Union
from typing import (
TYPE_CHECKING, ContextManager, Generator, List, NoReturn, Optional, Tuple,
Union,
)

import pytest

Expand Down Expand Up @@ -46,25 +49,20 @@
from .lazy_django import django_settings_is_configured, skip_if_no_django


TYPE_CHECKING = False
if TYPE_CHECKING:
from typing import ContextManager, NoReturn

import django


SETTINGS_MODULE_ENV = "DJANGO_SETTINGS_MODULE"
CONFIGURATION_ENV = "DJANGO_CONFIGURATION"
INVALID_TEMPLATE_VARS_ENV = "FAIL_INVALID_TEMPLATE_VARS"

_report_header = []


# ############### pytest hooks ################


@pytest.hookimpl()
def pytest_addoption(parser) -> None:
def pytest_addoption(parser: pytest.Parser) -> None:
group = parser.getgroup("django")
group.addoption(
"--reuse-db",
Expand Down Expand Up @@ -257,10 +255,13 @@ def _get_boolean_value(
)


report_header_key = pytest.StashKey[List[str]]()


@pytest.hookimpl()
def pytest_load_initial_conftests(
early_config,
parser,
early_config: pytest.Config,
parser: pytest.Parser,
args: List[str],
) -> None:
# Register the marks
Expand Down Expand Up @@ -330,12 +331,15 @@ def _get_option_with_source(
ds, ds_source = _get_option_with_source(options.ds, SETTINGS_MODULE_ENV)
dc, dc_source = _get_option_with_source(options.dc, CONFIGURATION_ENV)

report_header: List[str] = []
early_config.stash[report_header_key] = report_header

if ds:
_report_header.append(f"settings: {ds} (from {ds_source})")
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})")
report_header.append(f"configuration: {dc} (from {dc_source})")
os.environ[CONFIGURATION_ENV] = dc

# Install the django-configurations importer
Expand All @@ -353,20 +357,21 @@ def _get_option_with_source(
_setup_django()


@pytest.hookimpl()
def pytest_report_header() -> Optional[List[str]]:
if _report_header:
return ["django: " + ", ".join(_report_header)]
return None


@pytest.hookimpl(trylast=True)
def pytest_configure() -> None:
# Allow Django settings to be configured in a user pytest_configure call,
# but make sure we call django.setup()
_setup_django()


@pytest.hookimpl()
def pytest_report_header(config: pytest.Config) -> Optional[List[str]]:
report_header = config.stash[report_header_key]
if report_header:
return ["django: " + ", ".join(report_header)]
return None


@pytest.hookimpl(tryfirst=True)
def pytest_collection_modifyitems(items: List[pytest.Item]) -> None:
# If Django is not configured we don't need to bother
Expand Down Expand Up @@ -411,7 +416,7 @@ def get_order_number(test: pytest.Item) -> int:


@pytest.fixture(autouse=True, scope="session")
def django_test_environment(request) -> None:
def django_test_environment(request: pytest.FixtureRequest) -> None:
"""
Ensure that Django is loaded and has its testing environment setup.

Expand Down Expand Up @@ -459,7 +464,7 @@ def django_db_blocker() -> "Optional[_DatabaseBlocker]":


@pytest.fixture(autouse=True)
def _django_db_marker(request) -> None:
def _django_db_marker(request: pytest.FixtureRequest) -> None:
"""Implement the django_db marker, internal to pytest-django."""
marker = request.node.get_closest_marker("django_db")
if marker:
Expand All @@ -468,7 +473,7 @@ def _django_db_marker(request) -> None:

@pytest.fixture(autouse=True, scope="class")
def _django_setup_unittest(
request,
request: pytest.FixtureRequest,
django_db_blocker: "_DatabaseBlocker",
) -> Generator[None, None, None]:
"""Setup a django unittest, internal to pytest-django."""
Expand Down Expand Up @@ -521,7 +526,7 @@ def mailoutbox(

@pytest.fixture(scope="function")
def django_mail_patch_dns(
monkeypatch,
monkeypatch: pytest.MonkeyPatch,
django_mail_dnsname: str,
) -> None:
from django.core import mail
Expand All @@ -535,7 +540,7 @@ def django_mail_dnsname() -> str:


@pytest.fixture(autouse=True, scope="function")
def _django_set_urlconf(request) -> None:
def _django_set_urlconf(request: pytest.FixtureRequest) -> None:
"""Apply the @pytest.mark.urls marker, internal to pytest-django."""
marker = request.node.get_closest_marker("urls")
if marker:
Expand Down Expand Up @@ -628,30 +633,27 @@ def __mod__(self, var: str) -> str:
else:
return msg

# TODO: use pytest.MonkeyPatch once pytest<6.2 is not supported anymore
NOT_SET = object()
changed = False
previous_value = NOT_SET
if (
os.environ.get(INVALID_TEMPLATE_VARS_ENV, "false") == "true"
and django_settings_is_configured()
):
from django.conf import settings as dj_settings
with pytest.MonkeyPatch.context() as mp:
if (
os.environ.get(INVALID_TEMPLATE_VARS_ENV, "false") == "true"
and django_settings_is_configured()
):
from django.conf import settings as dj_settings

if dj_settings.TEMPLATES:
previous_value = dj_settings.TEMPLATES[0]["OPTIONS"].get("string_if_invalid", NOT_SET)
dj_settings.TEMPLATES[0]["OPTIONS"]["string_if_invalid"] = InvalidVarException()
changed = True
yield
if changed:
if previous_value is NOT_SET:
del dj_settings.TEMPLATES[0]["OPTIONS"]["string_if_invalid"]
else:
dj_settings.TEMPLATES[0]["OPTIONS"]["string_if_invalid"] = previous_value
if dj_settings.TEMPLATES:
mp.setitem(
dj_settings.TEMPLATES[0]["OPTIONS"],
"string_if_invalid",
InvalidVarException(),
)
yield


@pytest.fixture(autouse=True)
def _template_string_if_invalid_marker(monkeypatch, request) -> None:
def _template_string_if_invalid_marker(
monkeypatch: pytest.MonkeyPatch,
request: pytest.FixtureRequest,
) -> None:
"""Apply the @pytest.mark.ignore_template_errors marker,
internal to pytest-django."""
marker = request.keywords.get("ignore_template_errors", None)
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ project_urls =
packages = pytest_django
python_requires = >=3.8
setup_requires = setuptools_scm>=5.0.0
install_requires = pytest>=5.4.0
install_requires = pytest>=7.0.0
zip_safe = no

[options.entry_points]
Expand Down
Empty file added tests/__init__.py
Empty file.
Loading