From 7f48374b4ddace279c3478e67aec52e25a1e1133 Mon Sep 17 00:00:00 2001 From: josh-stableprice <46674140+josh-stableprice@users.noreply.github.com> Date: Wed, 23 Jun 2021 17:03:11 +0100 Subject: [PATCH 1/8] Use Test Case Class specified in Django settings --- pytest_django/fixtures.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index b58aadeb..7edf2bb7 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -41,6 +41,16 @@ "django_capture_on_commit_callbacks", ] +def import_from_string(val, setting_name): + """ + Attempt to import a class from a string representation. + """ + try: + return import_string(val) + except ImportError as e: + msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e) + raise ImportError(msg) + @pytest.fixture(scope="session") def django_db_modify_db_settings_tox_suffix() -> None: @@ -159,9 +169,10 @@ def _django_db_fixture_helper( import django.db if transactional: - test_case_class = django.test.TransactionTestCase + test_case_classname = settings.PYTEST.get("PYTEST_TRANSACTION_TEST_CASE", "django.test.TransactionTestCase") else: - test_case_class = django.test.TestCase + test_case_classname = settings.PYTEST.get("PYTEST_TEST_CASE", "django.test.TestCase") + test_case_class = import_string(test_case_classname) _reset_sequences = reset_sequences From b9ce92636bfe29ef367b5669db811861b0bf91fa Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 23 Jun 2021 17:11:33 +0100 Subject: [PATCH 2/8] Fix missing import and remove now redundant imports --- pytest_django/fixtures.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 7edf2bb7..849d2a54 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -5,6 +5,7 @@ from functools import partial import pytest +from django.utils.module_loading import import_string from . import live_server_helper from .django_compat import is_django_unittest @@ -165,9 +166,6 @@ def _django_db_fixture_helper( django_db_blocker.unblock() request.addfinalizer(django_db_blocker.restore) - import django.test - import django.db - if transactional: test_case_classname = settings.PYTEST.get("PYTEST_TRANSACTION_TEST_CASE", "django.test.TransactionTestCase") else: From a2e4cb5383c4b5c66eaff799262f95d059920a63 Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 23 Jun 2021 17:15:08 +0100 Subject: [PATCH 3/8] Fix missing import and remove now redundant imports --- pytest_django/fixtures.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 849d2a54..dbba68dd 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -42,6 +42,7 @@ "django_capture_on_commit_callbacks", ] + def import_from_string(val, setting_name): """ Attempt to import a class from a string representation. @@ -49,7 +50,8 @@ def import_from_string(val, setting_name): try: return import_string(val) except ImportError as e: - msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e) + msg = f"Could not import '{val}' for API setting " \ + f"'{setting_name}'. {e.__class__.__name__}: {e}." raise ImportError(msg) @@ -167,9 +169,15 @@ def _django_db_fixture_helper( request.addfinalizer(django_db_blocker.restore) if transactional: - test_case_classname = settings.PYTEST.get("PYTEST_TRANSACTION_TEST_CASE", "django.test.TransactionTestCase") + test_case_classname = settings.PYTEST.get( + "PYTEST_TRANSACTION_TEST_CASE", + "django.test.TransactionTestCase" + ) else: - test_case_classname = settings.PYTEST.get("PYTEST_TEST_CASE", "django.test.TestCase") + test_case_classname = settings.PYTEST.get( + "PYTEST_TEST_CASE", + "django.test.TestCase" + ) test_case_class = import_string(test_case_classname) _reset_sequences = reset_sequences From 23e1e8c9b712f9985d09e9ea256bd49f9aaaa6e9 Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 23 Jun 2021 17:18:19 +0100 Subject: [PATCH 4/8] Fix missing import and remove now redundant imports --- pytest_django/fixtures.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index dbba68dd..5d700fbf 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -154,6 +154,8 @@ def _django_db_fixture_helper( transactional: bool = False, reset_sequences: bool = False, ) -> None: + from django.conf import settings + if is_django_unittest(request): return @@ -169,13 +171,15 @@ def _django_db_fixture_helper( request.addfinalizer(django_db_blocker.restore) if transactional: - test_case_classname = settings.PYTEST.get( - "PYTEST_TRANSACTION_TEST_CASE", + test_case_classname = getattr( + settings, + "PYTEST_TRANSACTION_TEST_CASE_CLASS", "django.test.TransactionTestCase" ) else: - test_case_classname = settings.PYTEST.get( - "PYTEST_TEST_CASE", + test_case_classname = getattr( + settings, + "PYTEST_TEST_CASE_CLASS", "django.test.TestCase" ) test_case_class = import_string(test_case_classname) From 0712717faa086577275402ff58e6edf645a7df32 Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 24 Jun 2021 09:33:40 +0100 Subject: [PATCH 5/8] Add settings to the plugin instead of trying to load settings from Django Settings --- pytest_django/fixtures.py | 94 +++++++++++++++++++-------------------- pytest_django/plugin.py | 10 +++++ 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 5d700fbf..bed87795 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -20,7 +20,6 @@ _DjangoDbDatabases = Optional[Union["Literal['__all__']", Iterable[str]]] _DjangoDb = Tuple[bool, bool, _DjangoDbDatabases] - __all__ = [ "django_db_setup", "db", @@ -77,15 +76,15 @@ def django_db_modify_db_settings_xdist_suffix(request) -> None: @pytest.fixture(scope="session") def django_db_modify_db_settings_parallel_suffix( - django_db_modify_db_settings_tox_suffix: None, - django_db_modify_db_settings_xdist_suffix: None, + django_db_modify_db_settings_tox_suffix: None, + django_db_modify_db_settings_xdist_suffix: None, ) -> None: skip_if_no_django() @pytest.fixture(scope="session") def django_db_modify_db_settings( - django_db_modify_db_settings_parallel_suffix: None, + django_db_modify_db_settings_parallel_suffix: None, ) -> None: skip_if_no_django() @@ -107,13 +106,13 @@ def django_db_createdb(request) -> bool: @pytest.fixture(scope="session") def django_db_setup( - request, - django_test_environment: None, - django_db_blocker, - django_db_use_migrations: bool, - django_db_keepdb: bool, - django_db_createdb: bool, - django_db_modify_db_settings: None, + request, + django_test_environment: None, + django_db_blocker, + django_db_use_migrations: bool, + django_db_keepdb: bool, + django_db_createdb: bool, + django_db_modify_db_settings: None, ) -> None: """Top level fixture to ensure test databases are available""" from django.test.utils import setup_databases, teardown_databases @@ -149,10 +148,10 @@ def teardown_database() -> None: def _django_db_fixture_helper( - request, - django_db_blocker, - transactional: bool = False, - reset_sequences: bool = False, + request, + django_db_blocker, + transactional: bool = False, + reset_sequences: bool = False, ) -> None: from django.conf import settings @@ -171,17 +170,14 @@ def _django_db_fixture_helper( request.addfinalizer(django_db_blocker.restore) if transactional: - test_case_classname = getattr( - settings, - "PYTEST_TRANSACTION_TEST_CASE_CLASS", - "django.test.TransactionTestCase" - ) + test_case_classname = request.config.getvalue("transaction_testcase_class") or os.getenv( + "DJANGO_TRANSACTION_TEST_CASE_CLASS" + ) or "django.test.TransactionTestCase" else: - test_case_classname = getattr( - settings, - "PYTEST_TEST_CASE_CLASS", - "django.test.TestCase" - ) + test_case_classname = request.config.getvalue("testcase_class") or os.getenv( + "DJANGO_TEST_CASE_CLASS" + ) or "django.test.TestCase" + test_case_class = import_string(test_case_classname) _reset_sequences = reset_sequences @@ -244,9 +240,9 @@ def _set_suffix_to_test_databases(suffix: str) -> None: @pytest.fixture(scope="function") def db( - request, - django_db_setup: None, - django_db_blocker, + request, + django_db_setup: None, + django_db_blocker, ) -> None: """Require a django test database. @@ -264,8 +260,8 @@ def db( if "django_db_reset_sequences" in request.fixturenames: request.getfixturevalue("django_db_reset_sequences") if ( - "transactional_db" in request.fixturenames - or "live_server" in request.fixturenames + "transactional_db" in request.fixturenames + or "live_server" in request.fixturenames ): request.getfixturevalue("transactional_db") else: @@ -274,9 +270,9 @@ def db( @pytest.fixture(scope="function") def transactional_db( - request, - django_db_setup: None, - django_db_blocker, + request, + django_db_setup: None, + django_db_blocker, ) -> None: """Require a django test database with transaction support. @@ -297,9 +293,9 @@ def transactional_db( @pytest.fixture(scope="function") def django_db_reset_sequences( - request, - django_db_setup: None, - django_db_blocker, + request, + django_db_setup: None, + django_db_blocker, ) -> None: """Require a transactional test database with sequence reset support. @@ -353,9 +349,9 @@ def django_username_field(django_user_model) -> str: @pytest.fixture() def admin_user( - db: None, - django_user_model, - django_username_field: str, + db: None, + django_user_model, + django_username_field: str, ): """A Django admin user. @@ -384,8 +380,8 @@ def admin_user( @pytest.fixture() def admin_client( - db: None, - admin_user, + db: None, + admin_user, ) -> "django.test.client.Client": """A Django test client logged in as an admin user.""" from django.test.client import Client @@ -517,11 +513,11 @@ def _live_server_helper(request) -> None: @contextmanager def _assert_num_queries( - config, - num: int, - exact: bool = True, - connection=None, - info=None, + config, + num: int, + exact: bool = True, + connection=None, + info=None, ) -> Generator["django.test.utils.CaptureQueriesContext", None, None]: from django.test.utils import CaptureQueriesContext @@ -568,9 +564,9 @@ def django_assert_max_num_queries(pytestconfig): @contextmanager def _capture_on_commit_callbacks( - *, - using: Optional[str] = None, - execute: bool = False + *, + using: Optional[str] = None, + execute: bool = False ): from django.db import DEFAULT_DB_ALIAS, connections from django.test import TestCase diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index 3e9dd9c6..a8eafa88 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -120,6 +120,16 @@ def pytest_addoption(parser) -> None: default=None, help="Address and port for the live_server fixture.", ) + group.addoption( + "--testcase", + default=None, + help="The base TestCase class to patch for use with django. Useful for hypothesis users", + ) + group.addoption( + "--transaction-testcase", + default=None, + help="The base TransactionTestCase class to patch for use with django. Useful for hypothesis users", + ) parser.addini( SETTINGS_MODULE_ENV, "Django settings module to use by pytest-django." ) From bf53cf7923a30d93f8a0a99767c050b9d46687e9 Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 24 Jun 2021 09:34:20 +0100 Subject: [PATCH 6/8] Add settings to the plugin instead of trying to load settings from Django Settings --- pytest_django/fixtures.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index bed87795..5536e0e8 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -153,7 +153,6 @@ def _django_db_fixture_helper( transactional: bool = False, reset_sequences: bool = False, ) -> None: - from django.conf import settings if is_django_unittest(request): return From 43324f5b2a683ade88ebc619ef4582c1af21d145 Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 24 Jun 2021 09:53:28 +0100 Subject: [PATCH 7/8] Add settings to the plugin instead of trying to load settings from Django Settings --- pytest_django/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index a8eafa88..e3514d53 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -121,12 +121,12 @@ def pytest_addoption(parser) -> None: help="Address and port for the live_server fixture.", ) group.addoption( - "--testcase", + "--testcase-class", default=None, help="The base TestCase class to patch for use with django. Useful for hypothesis users", ) group.addoption( - "--transaction-testcase", + "--transaction-testcase-class", default=None, help="The base TransactionTestCase class to patch for use with django. Useful for hypothesis users", ) From 556fcbd79afdfc1dc13e294beff523667f7eaf47 Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 24 Jun 2021 10:00:51 +0100 Subject: [PATCH 8/8] Fix issue with f strings not existing in earlier versions of python and max line length being smaller --- pytest_django/fixtures.py | 4 ++-- pytest_django/plugin.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 5536e0e8..5b7adf11 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -49,8 +49,8 @@ def import_from_string(val, setting_name): try: return import_string(val) except ImportError as e: - msg = f"Could not import '{val}' for API setting " \ - f"'{setting_name}'. {e.__class__.__name__}: {e}." + msg = "Could not import '%s' for API setting '%s'. %s: %s." \ + % (val, setting_name, e.__class__.__name__, e) raise ImportError(msg) diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index e3514d53..75f2c12f 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -128,7 +128,8 @@ def pytest_addoption(parser) -> None: group.addoption( "--transaction-testcase-class", default=None, - help="The base TransactionTestCase class to patch for use with django. Useful for hypothesis users", + help="The base TransactionTestCase class to patch for use with django. " + "Useful for hypothesis users", ) parser.addini( SETTINGS_MODULE_ENV, "Django settings module to use by pytest-django."