From f80b4b796cc68f4f3c904087d6a242774989e5a1 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 12:51:37 -0500 Subject: [PATCH 01/31] Update fixtures.py --- pytest_django/fixtures.py | 77 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 39c84b60..aee949e7 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -597,6 +597,41 @@ def _live_server_helper(request: pytest.FixtureRequest) -> Generator[None, None, live_server._live_server_modified_settings.disable() +class CaptureAllConnectionsQueriesContext: + """ + Context manager that captures all queries executed by Django ORM across all Databases in settings.DATABASES. + """ + + def __init__(self): + from django.db import connections + self.contexts = {alias: CaptureQueriesContext(connections[alias]) for alias in connections} + + def __iter__(self): + return iter(self.captured_queries) + + def __getitem__(self, index): + return self.captured_queries[index] + + def __len__(self): + return len(self.captured_queries) + + @property + def captured_queries(self): + queries = [] + for context in self.contexts.values(): + queries.extend(context.captured_queries) + return queries + + def __enter__(self): + for context in self.contexts.values(): + context.__enter__() + return self + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + for context in self.contexts.values(): + context.__exit__(exc_type, exc_val, exc_tb) + + class DjangoAssertNumQueries(Protocol): """The type of the `django_assert_num_queries` and `django_assert_max_num_queries` fixtures.""" @@ -624,7 +659,6 @@ def _assert_num_queries( ) -> Generator[django.test.utils.CaptureQueriesContext, None, None]: from django.db import connection as default_conn, connections from django.test.utils import CaptureQueriesContext - if connection and using: raise ValueError('The "connection" and "using" parameter cannot be used together') @@ -635,9 +669,33 @@ def _assert_num_queries( else: conn = default_conn - verbose = config.getoption("verbose") > 0 with CaptureQueriesContext(conn) as context: - yield context + yield _assert_num_queries_context(config=config, context=context, num=num, exact=exact, info=info) + + +@contextmanager +def _assert_num_queries_all_db( + config, + num: int, + exact: bool = True, + info: str | None = None, +) -> Generator[CaptureAllConnectionsQueriesContext, None, None]: + """A recreation of pytest-django's assert_num_queries that works with all databases in settings.Databases.""" + + with CaptureAllConnectionsQueriesContext() as context: + yield _assert_num_queries_context(config=config, context=context, num=num, exact=exact, info=info) + + +def _assert_num_queries_context( + *, + config: pytest.Config, + context: CaptureQueriesContext | CaptureAllConnectionsQueriesContext, + num: int, + exact: bool = True, + info: str | None = None, +) -> Generator[django.test.utils.CaptureQueriesContext | CaptureAllConnectionsQueriesContext, None, None]: + verbose = config.getoption("verbose") > 0 + with context: num_performed = len(context) if exact: failed = num != num_performed @@ -657,6 +715,7 @@ def _assert_num_queries( else: msg += " (add -v option to show queries)" pytest.fail(msg) + yield context @pytest.fixture() @@ -671,6 +730,18 @@ def django_assert_max_num_queries(pytestconfig: pytest.Config) -> DjangoAssertNu return partial(_assert_num_queries, pytestconfig, exact=False) +@pytest.fixture(scope="function") +def django_assert_num_queries_all_connections(pytestconfig): + """Asserts that the number of queries executed by Django ORM across all connections in settings.DATABASES is equal to the given number.""" + return partial(_assert_num_queries_all_db, pytestconfig) + + +@pytest.fixture(scope="function") +def django_assert_max_num_queries_all_connections(pytestconfig): + """Asserts that the number of queries executed by Django ORM across all connections in settings.DATABASES is less than or equal to the given number.""" + return partial(_assert_num_queries_all_db, pytestconfig, exact=False) + + class DjangoCaptureOnCommitCallbacks(Protocol): """The type of the `django_capture_on_commit_callbacks` fixture.""" From 7c886878f13b4ee8802830d3b2092071ceeeea6c Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 18:57:39 +0100 Subject: [PATCH 02/31] Fix linter --- 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 aee949e7..5441e430 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -604,6 +604,7 @@ class CaptureAllConnectionsQueriesContext: def __init__(self): from django.db import connections + self.contexts = {alias: CaptureQueriesContext(connections[alias]) for alias in connections} def __iter__(self): @@ -659,6 +660,7 @@ def _assert_num_queries( ) -> Generator[django.test.utils.CaptureQueriesContext, None, None]: from django.db import connection as default_conn, connections from django.test.utils import CaptureQueriesContext + if connection and using: raise ValueError('The "connection" and "using" parameter cannot be used together') @@ -670,7 +672,9 @@ def _assert_num_queries( conn = default_conn with CaptureQueriesContext(conn) as context: - yield _assert_num_queries_context(config=config, context=context, num=num, exact=exact, info=info) + yield _assert_num_queries_context( + config=config, context=context, num=num, exact=exact, info=info + ) @contextmanager @@ -683,7 +687,9 @@ def _assert_num_queries_all_db( """A recreation of pytest-django's assert_num_queries that works with all databases in settings.Databases.""" with CaptureAllConnectionsQueriesContext() as context: - yield _assert_num_queries_context(config=config, context=context, num=num, exact=exact, info=info) + yield _assert_num_queries_context( + config=config, context=context, num=num, exact=exact, info=info + ) def _assert_num_queries_context( @@ -693,7 +699,9 @@ def _assert_num_queries_context( num: int, exact: bool = True, info: str | None = None, -) -> Generator[django.test.utils.CaptureQueriesContext | CaptureAllConnectionsQueriesContext, None, None]: +) -> Generator[ + django.test.utils.CaptureQueriesContext | CaptureAllConnectionsQueriesContext, None, None +]: verbose = config.getoption("verbose") > 0 with context: num_performed = len(context) From dad88dc1368889d1b85adc9f9ddc4ee7888abce0 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:00:34 +0100 Subject: [PATCH 03/31] oops --- pytest_django/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 5441e430..09e661fd 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -704,6 +704,7 @@ def _assert_num_queries_context( ]: verbose = config.getoption("verbose") > 0 with context: + yield context num_performed = len(context) if exact: failed = num != num_performed @@ -723,7 +724,6 @@ def _assert_num_queries_context( else: msg += " (add -v option to show queries)" pytest.fail(msg) - yield context @pytest.fixture() From 59b64774019ee239d7ad4968b426477061100955 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:03:48 +0100 Subject: [PATCH 04/31] ..? --- pytest_django/fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 09e661fd..3c98115d 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -672,7 +672,7 @@ def _assert_num_queries( conn = default_conn with CaptureQueriesContext(conn) as context: - yield _assert_num_queries_context( + yield from _assert_num_queries_context( config=config, context=context, num=num, exact=exact, info=info ) @@ -687,7 +687,7 @@ def _assert_num_queries_all_db( """A recreation of pytest-django's assert_num_queries that works with all databases in settings.Databases.""" with CaptureAllConnectionsQueriesContext() as context: - yield _assert_num_queries_context( + yield from _assert_num_queries_context( config=config, context=context, num=num, exact=exact, info=info ) From 53cc21c6d9ac3163b6cf17ecfb868431a7ee3d79 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:14:38 +0100 Subject: [PATCH 05/31] basic tests --- tests/test_fixtures.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 39c6666f..cae9f187 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -259,6 +259,38 @@ def test_queries(django_assert_num_queries): assert result.ret == 1 +@pytest.mark.django_db(databases=["default", "replica", "second"]) +def test_django_assert_num_queries_all_connections() -> None: + with django_assert_num_queries_all_connections(3): + Item.objects.count() + Item.objects.using("replica").count() + Item.objects.using("second").count() + + +@pytest.mark.django_db(databases=["default", "replica", "second"]) +def test_django_assert_max_num_queries_all_connections( + request: pytest.FixtureRequest, + django_assert_max_num_queries_all_connections: DjangoAssertNumQueries, +) -> None: + with nonverbose_config(request.config): + with django_assert_max_num_queries(2): + Item.objects.create(name="1-foo") + Item.objects.using("second").create(name="2-bar") + + with pytest.raises(pytest.fail.Exception) as excinfo: # noqa: PT012 + with django_assert_max_num_queries(2) as captured: + Item.objects.create(name="1-foo") + Item.objects.create(name="2-bar") + Item.objects.using("second").create(name="3-quux") + + assert excinfo.value.args == ( + "Expected to perform 2 queries or less but 3 were done " + "(add -v option to show queries)", + ) + assert len(captured.captured_queries) == 3 + assert "1-foo" in captured.captured_queries[0]["sql"] + + @pytest.mark.django_db def test_django_capture_on_commit_callbacks( django_capture_on_commit_callbacks: DjangoCaptureOnCommitCallbacks, From f4d486e3ed7397d0f7167daf15813e93e2833f1e Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:17:35 +0100 Subject: [PATCH 06/31] . --- pytest_django/fixtures.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 3c98115d..42631acc 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -602,22 +602,22 @@ class CaptureAllConnectionsQueriesContext: Context manager that captures all queries executed by Django ORM across all Databases in settings.DATABASES. """ - def __init__(self): + def __init__(self) -> None: from django.db import connections self.contexts = {alias: CaptureQueriesContext(connections[alias]) for alias in connections} - def __iter__(self): + def __iter__(self) -> None: return iter(self.captured_queries) - def __getitem__(self, index): + def __getitem__(self, index: int) -> dict[str, Any]: return self.captured_queries[index] - def __len__(self): + def __len__(self) -> int: return len(self.captured_queries) @property - def captured_queries(self): + def captured_queries(self) -> dict[str, Any]: queries = [] for context in self.contexts.values(): queries.extend(context.captured_queries) @@ -695,7 +695,7 @@ def _assert_num_queries_all_db( def _assert_num_queries_context( *, config: pytest.Config, - context: CaptureQueriesContext | CaptureAllConnectionsQueriesContext, + context: django.test.utils.CaptureQueriesContext | CaptureAllConnectionsQueriesContext, num: int, exact: bool = True, info: str | None = None, From 2b7bc67b1fee0217d32fbcb0ff9cf055db609cc7 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:20:04 +0100 Subject: [PATCH 07/31] . --- pytest_django/fixtures.py | 2 ++ pytest_django/plugin.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 42631acc..da19eca9 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -50,6 +50,8 @@ "db", "django_assert_max_num_queries", "django_assert_num_queries", + "django_assert_max_num_queries_all_connections", + "django_assert_num_queries_all_connections", "django_capture_on_commit_callbacks", "django_db_reset_sequences", "django_db_serialized_rollback", diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index 08d961a6..aee4b98e 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -29,6 +29,8 @@ db, # noqa: F401 django_assert_max_num_queries, # noqa: F401 django_assert_num_queries, # noqa: F401 + django_assert_max_num_queries_all_connections, # noqa: F401 + django_assert_num_queries_all_connections, # noqa: F401 django_capture_on_commit_callbacks, # noqa: F401 django_db_createdb, # noqa: F401 django_db_keepdb, # noqa: F401 From 975a6a834d28b45371ca870b2fe3ced91b52879f Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:21:19 +0100 Subject: [PATCH 08/31] import . --- pytest_django/fixtures.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index da19eca9..b8dc3887 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -606,6 +606,7 @@ class CaptureAllConnectionsQueriesContext: def __init__(self) -> None: from django.db import connections + from django.test.utils import CaptureQueriesContext self.contexts = {alias: CaptureQueriesContext(connections[alias]) for alias in connections} From 8d049eb2e4a46801a0d23eb0e74531cdf5242383 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:22:07 +0100 Subject: [PATCH 09/31] . --- pytest_django/fixtures.py | 2 +- pytest_django/plugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index b8dc3887..46fcb12e 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -49,8 +49,8 @@ "client", "db", "django_assert_max_num_queries", - "django_assert_num_queries", "django_assert_max_num_queries_all_connections", + "django_assert_num_queries", "django_assert_num_queries_all_connections", "django_capture_on_commit_callbacks", "django_db_reset_sequences", diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index aee4b98e..e22045b9 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -28,8 +28,8 @@ client, # noqa: F401 db, # noqa: F401 django_assert_max_num_queries, # noqa: F401 - django_assert_num_queries, # noqa: F401 django_assert_max_num_queries_all_connections, # noqa: F401 + django_assert_num_queries, # noqa: F401 django_assert_num_queries_all_connections, # noqa: F401 django_capture_on_commit_callbacks, # noqa: F401 django_db_createdb, # noqa: F401 From 29463f2b2feb4bc71ef4f2341bd71ed81697e372 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:26:21 +0100 Subject: [PATCH 10/31] . --- pytest_django/fixtures.py | 4 ++-- tests/test_fixtures.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 46fcb12e..9ddaf0e0 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -610,7 +610,7 @@ def __init__(self) -> None: self.contexts = {alias: CaptureQueriesContext(connections[alias]) for alias in connections} - def __iter__(self) -> None: + def __iter__(self) -> Iterable[dict[str, Any]]: return iter(self.captured_queries) def __getitem__(self, index: int) -> dict[str, Any]: @@ -620,7 +620,7 @@ def __len__(self) -> int: return len(self.captured_queries) @property - def captured_queries(self) -> dict[str, Any]: + def captured_queries(self) -> list[dict[str, Any]]: queries = [] for context in self.contexts.values(): queries.extend(context.captured_queries) diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index cae9f187..9daace02 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -273,12 +273,12 @@ def test_django_assert_max_num_queries_all_connections( django_assert_max_num_queries_all_connections: DjangoAssertNumQueries, ) -> None: with nonverbose_config(request.config): - with django_assert_max_num_queries(2): + with django_assert_max_num_queries_all_connections(2): Item.objects.create(name="1-foo") Item.objects.using("second").create(name="2-bar") with pytest.raises(pytest.fail.Exception) as excinfo: # noqa: PT012 - with django_assert_max_num_queries(2) as captured: + with django_assert_max_num_queries_all_connections(2) as captured: Item.objects.create(name="1-foo") Item.objects.create(name="2-bar") Item.objects.using("second").create(name="3-quux") From 17a103f0c2706efb02ee11330d323667160252c0 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:31:16 +0100 Subject: [PATCH 11/31] . --- pytest_django/fixtures.py | 16 ++++++++++++++-- tests/test_fixtures.py | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 9ddaf0e0..0afb48a9 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -651,6 +651,18 @@ def __call__( pass # pragma: no cover +class DjangoAssertNumAllConnectionsQueries(Protocol): + """The type of the `django_assert_num_queries_all_connections` and + `django_assert_max_num_queries_all_connections` fixtures.""" + + def __call__( + self, + num: int, + info: str | None = ..., + ) -> CaptureAllConnectionsQueriesContext: + pass # pragma: no cover + + @contextmanager def _assert_num_queries( config: pytest.Config, @@ -742,13 +754,13 @@ def django_assert_max_num_queries(pytestconfig: pytest.Config) -> DjangoAssertNu @pytest.fixture(scope="function") -def django_assert_num_queries_all_connections(pytestconfig): +def django_assert_num_queries_all_connections(pytestconfig: pytest.Config) -> DjangoAssertNumAllConnectionsQueries: """Asserts that the number of queries executed by Django ORM across all connections in settings.DATABASES is equal to the given number.""" return partial(_assert_num_queries_all_db, pytestconfig) @pytest.fixture(scope="function") -def django_assert_max_num_queries_all_connections(pytestconfig): +def django_assert_max_num_queries_all_connections(pytestconfig: pytest.Config) -> DjangoAssertNumAllConnectionsQueries: """Asserts that the number of queries executed by Django ORM across all connections in settings.DATABASES is less than or equal to the given number.""" return partial(_assert_num_queries_all_db, pytestconfig, exact=False) diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 9daace02..3ce734f3 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -260,7 +260,7 @@ def test_queries(django_assert_num_queries): @pytest.mark.django_db(databases=["default", "replica", "second"]) -def test_django_assert_num_queries_all_connections() -> None: +def test_django_assert_num_queries_all_connections(django_assert_num_queries_all_connections) -> None: with django_assert_num_queries_all_connections(3): Item.objects.count() Item.objects.using("replica").count() From 7a7288b62787965ecb9085df19ce53d9ffa5663f Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:34:50 +0100 Subject: [PATCH 12/31] . --- pytest_django/fixtures.py | 8 ++++++-- tests/test_fixtures.py | 8 +++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 0afb48a9..1a5af815 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -754,13 +754,17 @@ def django_assert_max_num_queries(pytestconfig: pytest.Config) -> DjangoAssertNu @pytest.fixture(scope="function") -def django_assert_num_queries_all_connections(pytestconfig: pytest.Config) -> DjangoAssertNumAllConnectionsQueries: +def django_assert_num_queries_all_connections( + pytestconfig: pytest.Config +) -> DjangoAssertNumAllConnectionsQueries: """Asserts that the number of queries executed by Django ORM across all connections in settings.DATABASES is equal to the given number.""" return partial(_assert_num_queries_all_db, pytestconfig) @pytest.fixture(scope="function") -def django_assert_max_num_queries_all_connections(pytestconfig: pytest.Config) -> DjangoAssertNumAllConnectionsQueries: +def django_assert_max_num_queries_all_connections( + pytestconfig: pytest.Config +) -> DjangoAssertNumAllConnectionsQueries: """Asserts that the number of queries executed by Django ORM across all connections in settings.DATABASES is less than or equal to the given number.""" return partial(_assert_num_queries_all_db, pytestconfig, exact=False) diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 3ce734f3..958c1338 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -20,7 +20,7 @@ from .helpers import DjangoPytester -from pytest_django import DjangoAssertNumQueries, DjangoCaptureOnCommitCallbacks, DjangoDbBlocker +from pytest_django import DjangoAssertNumQueries, DjangoAssertNumAllConnectionsQueries, DjangoCaptureOnCommitCallbacks, DjangoDbBlocker from pytest_django_test.app.models import Item @@ -260,7 +260,9 @@ def test_queries(django_assert_num_queries): @pytest.mark.django_db(databases=["default", "replica", "second"]) -def test_django_assert_num_queries_all_connections(django_assert_num_queries_all_connections) -> None: +def test_django_assert_num_queries_all_connections( + django_assert_num_queries_all_connections: DjangoAssertNumAllConnectionsQueries +) -> None: with django_assert_num_queries_all_connections(3): Item.objects.count() Item.objects.using("replica").count() @@ -269,7 +271,7 @@ def test_django_assert_num_queries_all_connections(django_assert_num_queries_all @pytest.mark.django_db(databases=["default", "replica", "second"]) def test_django_assert_max_num_queries_all_connections( - request: pytest.FixtureRequest, + request: pytest.FixtureRequest: DjangoAssertNumAllConnectionsQueries, django_assert_max_num_queries_all_connections: DjangoAssertNumQueries, ) -> None: with nonverbose_config(request.config): From f49d8b815fbfd83df554474e73b9d00a915ce210 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:36:02 +0100 Subject: [PATCH 13/31] . --- pytest_django/fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 1a5af815..3c8cbf14 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -755,7 +755,7 @@ def django_assert_max_num_queries(pytestconfig: pytest.Config) -> DjangoAssertNu @pytest.fixture(scope="function") def django_assert_num_queries_all_connections( - pytestconfig: pytest.Config + pytestconfig: pytest.Config, ) -> DjangoAssertNumAllConnectionsQueries: """Asserts that the number of queries executed by Django ORM across all connections in settings.DATABASES is equal to the given number.""" return partial(_assert_num_queries_all_db, pytestconfig) @@ -763,7 +763,7 @@ def django_assert_num_queries_all_connections( @pytest.fixture(scope="function") def django_assert_max_num_queries_all_connections( - pytestconfig: pytest.Config + pytestconfig: pytest.Config, ) -> DjangoAssertNumAllConnectionsQueries: """Asserts that the number of queries executed by Django ORM across all connections in settings.DATABASES is less than or equal to the given number.""" return partial(_assert_num_queries_all_db, pytestconfig, exact=False) From 21201eac10b15123dd43864b18ad625ef34865d2 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:36:57 +0100 Subject: [PATCH 14/31] . --- tests/test_fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 958c1338..2b36963f 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -271,8 +271,8 @@ def test_django_assert_num_queries_all_connections( @pytest.mark.django_db(databases=["default", "replica", "second"]) def test_django_assert_max_num_queries_all_connections( - request: pytest.FixtureRequest: DjangoAssertNumAllConnectionsQueries, - django_assert_max_num_queries_all_connections: DjangoAssertNumQueries, + request: pytest.FixtureRequest, + django_assert_max_num_queries_all_connections: DjangoAssertNumAllConnectionsQueries, ) -> None: with nonverbose_config(request.config): with django_assert_max_num_queries_all_connections(2): From a021aa33abe1e306bedde8071335fb2dbbc9af4a Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:39:36 +0100 Subject: [PATCH 15/31] . --- pytest_django/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytest_django/__init__.py b/pytest_django/__init__.py index e4bb08f5..9ac12ed7 100644 --- a/pytest_django/__init__.py +++ b/pytest_django/__init__.py @@ -5,12 +5,13 @@ __version__ = "unknown" -from .fixtures import DjangoAssertNumQueries, DjangoCaptureOnCommitCallbacks +from .fixtures import DjangoAssertNumQueries, DjangoAssertNumAllConnectionsQueries, DjangoCaptureOnCommitCallbacks from .plugin import DjangoDbBlocker __all__ = [ "DjangoAssertNumQueries", + "DjangoAssertNumAllConnectionsQueries", "DjangoCaptureOnCommitCallbacks", "DjangoDbBlocker", "__version__", From 6bd69c6b7d139a6a5c1fd8372936f5e1d06587e4 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:42:32 +0100 Subject: [PATCH 16/31] . --- pytest_django/__init__.py | 8 ++++++-- tests/test_fixtures.py | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pytest_django/__init__.py b/pytest_django/__init__.py index 9ac12ed7..b673213a 100644 --- a/pytest_django/__init__.py +++ b/pytest_django/__init__.py @@ -5,13 +5,17 @@ __version__ = "unknown" -from .fixtures import DjangoAssertNumQueries, DjangoAssertNumAllConnectionsQueries, DjangoCaptureOnCommitCallbacks +from .fixtures import ( + DjangoAssertNumAllConnectionsQueries, + DjangoAssertNumQueries, + DjangoCaptureOnCommitCallbacks, +) from .plugin import DjangoDbBlocker __all__ = [ - "DjangoAssertNumQueries", "DjangoAssertNumAllConnectionsQueries", + "DjangoAssertNumQueries", "DjangoCaptureOnCommitCallbacks", "DjangoDbBlocker", "__version__", diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 2b36963f..57cf95a1 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -20,7 +20,12 @@ from .helpers import DjangoPytester -from pytest_django import DjangoAssertNumQueries, DjangoAssertNumAllConnectionsQueries, DjangoCaptureOnCommitCallbacks, DjangoDbBlocker +from pytest_django import ( + DjangoAssertNumAllConnectionsQueries, + DjangoAssertNumQueries, + DjangoCaptureOnCommitCallbacks, + DjangoDbBlocker, +) from pytest_django_test.app.models import Item From 116adeff66638fdee414d1404579c785953bfd30 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:45:03 +0100 Subject: [PATCH 17/31] trim whitespace --- pytest_django/__init__.py | 4 ++-- tests/test_fixtures.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pytest_django/__init__.py b/pytest_django/__init__.py index b673213a..16008922 100644 --- a/pytest_django/__init__.py +++ b/pytest_django/__init__.py @@ -6,8 +6,8 @@ from .fixtures import ( - DjangoAssertNumAllConnectionsQueries, - DjangoAssertNumQueries, + DjangoAssertNumAllConnectionsQueries, + DjangoAssertNumQueries, DjangoCaptureOnCommitCallbacks, ) from .plugin import DjangoDbBlocker diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 57cf95a1..bc383b64 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -21,9 +21,9 @@ from .helpers import DjangoPytester from pytest_django import ( - DjangoAssertNumAllConnectionsQueries, - DjangoAssertNumQueries, - DjangoCaptureOnCommitCallbacks, + DjangoAssertNumAllConnectionsQueries, + DjangoAssertNumQueries, + DjangoCaptureOnCommitCallbacks, DjangoDbBlocker, ) from pytest_django_test.app.models import Item From d5944eb334ab9508196b063679d32f8addd0231d Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 19:46:39 +0100 Subject: [PATCH 18/31] . --- tests/test_fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index bc383b64..f9c2b65d 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -266,7 +266,7 @@ def test_queries(django_assert_num_queries): @pytest.mark.django_db(databases=["default", "replica", "second"]) def test_django_assert_num_queries_all_connections( - django_assert_num_queries_all_connections: DjangoAssertNumAllConnectionsQueries + django_assert_num_queries_all_connections: DjangoAssertNumAllConnectionsQueries, ) -> None: with django_assert_num_queries_all_connections(3): Item.objects.count() From be5ff4af70986a8969178982b1cb691853049471 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:07:47 +0100 Subject: [PATCH 19/31] . --- pytest_django/fixtures.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 3c8cbf14..fd64b9ea 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -38,6 +38,7 @@ _DjangoDbAvailableApps = Optional[List[str]] # transaction, reset_sequences, databases, serialized_rollback, available_apps _DjangoDb = Tuple[bool, bool, _DjangoDbDatabases, bool, _DjangoDbAvailableApps] +_QueriesContext = TypeVar("T", CaptureQueriesContext, CaptureAllConnectionsQueriesContext) __all__ = [ @@ -648,7 +649,7 @@ def __call__( *, using: str | None = ..., ) -> django.test.utils.CaptureQueriesContext: - pass # pragma: no cover + ... class DjangoAssertNumAllConnectionsQueries(Protocol): @@ -660,7 +661,7 @@ def __call__( num: int, info: str | None = ..., ) -> CaptureAllConnectionsQueriesContext: - pass # pragma: no cover + ... @contextmanager @@ -710,13 +711,11 @@ def _assert_num_queries_all_db( def _assert_num_queries_context( *, config: pytest.Config, - context: django.test.utils.CaptureQueriesContext | CaptureAllConnectionsQueriesContext, + context: _QueriesContext, num: int, exact: bool = True, info: str | None = None, -) -> Generator[ - django.test.utils.CaptureQueriesContext | CaptureAllConnectionsQueriesContext, None, None -]: +) -> Generator[_QueriesContext]: verbose = config.getoption("verbose") > 0 with context: yield context From b257ff64fdc4f13f7ff0c4ae88ffed0d79700e8e Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:15:57 +0100 Subject: [PATCH 20/31] . --- pytest_django/fixtures.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index fd64b9ea..cbb6132c 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -17,6 +17,7 @@ Optional, Protocol, Tuple, + runtime_checkable, Union, ) @@ -38,7 +39,6 @@ _DjangoDbAvailableApps = Optional[List[str]] # transaction, reset_sequences, databases, serialized_rollback, available_apps _DjangoDb = Tuple[bool, bool, _DjangoDbDatabases, bool, _DjangoDbAvailableApps] -_QueriesContext = TypeVar("T", CaptureQueriesContext, CaptureAllConnectionsQueriesContext) __all__ = [ @@ -66,6 +66,22 @@ ] +@runtime_checkable +class QueryCaptureContextProtocol(Protocol): + @property + def captured_queries(self) -> List[Dict[str, Any]]: + ... + + def __enter__(self) -> "QueryCaptureContextProtocol": + ... + + def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: + ... + + +_QueriesContext = TypeVar("_QueriesContext", bound=QueryCaptureContextProtocol) + + @pytest.fixture(scope="session") def django_db_modify_db_settings_tox_suffix() -> None: skip_if_no_django() @@ -715,7 +731,7 @@ def _assert_num_queries_context( num: int, exact: bool = True, info: str | None = None, -) -> Generator[_QueriesContext]: +) -> ContextManager[_QueriesContext]: verbose = config.getoption("verbose") > 0 with context: yield context From b73ad365d40473997af4199cf6a32f9aca7a9fc2 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:18:08 +0100 Subject: [PATCH 21/31] . --- pytest_django/fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index cbb6132c..4aac1240 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -664,7 +664,7 @@ def __call__( info: str | None = ..., *, using: str | None = ..., - ) -> django.test.utils.CaptureQueriesContext: + ) -> ContextManager[django.test.utils.CaptureQueriesContext]: ... @@ -676,7 +676,7 @@ def __call__( self, num: int, info: str | None = ..., - ) -> CaptureAllConnectionsQueriesContext: + ) -> ContextManager[CaptureAllConnectionsQueriesContext]: ... From 8d17f10d315850e4571708de69e4540932c81ebf Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:19:20 +0100 Subject: [PATCH 22/31] . --- pytest_django/fixtures.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 4aac1240..26395041 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -16,6 +16,7 @@ Literal, Optional, Protocol, + TypeVar, Tuple, runtime_checkable, Union, From 1813e9ec34cbdf70c877adad4d632bc173fb8a41 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:20:55 +0100 Subject: [PATCH 23/31] . --- pytest_django/fixtures.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 26395041..98fce9e2 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -16,10 +16,10 @@ Literal, Optional, Protocol, - TypeVar, Tuple, - runtime_checkable, + TypeVar, Union, + runtime_checkable, ) import pytest @@ -73,7 +73,7 @@ class QueryCaptureContextProtocol(Protocol): def captured_queries(self) -> List[Dict[str, Any]]: ... - def __enter__(self) -> "QueryCaptureContextProtocol": + def __enter__(self) -> QueryCaptureContextProtocol: ... def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: From 01437f86e434abb4bda8158bbff6f24ec9103828 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:23:05 +0100 Subject: [PATCH 24/31] . --- pytest_django/fixtures.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 98fce9e2..b560afff 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -70,14 +70,11 @@ @runtime_checkable class QueryCaptureContextProtocol(Protocol): @property - def captured_queries(self) -> List[Dict[str, Any]]: - ... + def captured_queries(self) -> List[Dict[str, Any]]: ... - def __enter__(self) -> QueryCaptureContextProtocol: - ... + def __enter__(self) -> QueryCaptureContextProtocol: ... - def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: - ... + def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: ... _QueriesContext = TypeVar("_QueriesContext", bound=QueryCaptureContextProtocol) @@ -665,8 +662,7 @@ def __call__( info: str | None = ..., *, using: str | None = ..., - ) -> ContextManager[django.test.utils.CaptureQueriesContext]: - ... + ) -> ContextManager[django.test.utils.CaptureQueriesContext]: ... class DjangoAssertNumAllConnectionsQueries(Protocol): @@ -677,8 +673,7 @@ def __call__( self, num: int, info: str | None = ..., - ) -> ContextManager[CaptureAllConnectionsQueriesContext]: - ... + ) -> ContextManager[CaptureAllConnectionsQueriesContext]: ... @contextmanager From 6b023768eae51c30e924d52648675dca866710bc Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:39:10 +0100 Subject: [PATCH 25/31] . --- pytest_django/fixtures.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index b560afff..2dbef837 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -10,6 +10,7 @@ Any, Callable, ContextManager, + Dict, Generator, Iterable, List, From cc3e190e6d8483962c4b6091ba9afcbcb1ae0b83 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:44:31 +0100 Subject: [PATCH 26/31] . --- pytest_django/fixtures.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 2dbef837..07e9ef66 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -13,6 +13,7 @@ Dict, Generator, Iterable, + Iterator, List, Literal, Optional, @@ -701,7 +702,7 @@ def _assert_num_queries( conn = default_conn with CaptureQueriesContext(conn) as context: - yield from _assert_num_queries_context( + yield _assert_num_queries_context( config=config, context=context, num=num, exact=exact, info=info ) @@ -716,7 +717,7 @@ def _assert_num_queries_all_db( """A recreation of pytest-django's assert_num_queries that works with all databases in settings.Databases.""" with CaptureAllConnectionsQueriesContext() as context: - yield from _assert_num_queries_context( + yield _assert_num_queries_context( config=config, context=context, num=num, exact=exact, info=info ) @@ -728,7 +729,7 @@ def _assert_num_queries_context( num: int, exact: bool = True, info: str | None = None, -) -> ContextManager[_QueriesContext]: +) -> Iterator[_QueriesContext]: verbose = config.getoption("verbose") > 0 with context: yield context From 33b91d64f8a46a265f6d54dde130ce24dfd7129c Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:47:13 +0100 Subject: [PATCH 27/31] . --- pytest_django/fixtures.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 07e9ef66..890c25ae 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from collections.abc import Sized from contextlib import contextmanager from functools import partial from typing import ( @@ -70,7 +71,7 @@ @runtime_checkable -class QueryCaptureContextProtocol(Protocol): +class QueryCaptureContextProtocol(Protocol, Sized): @property def captured_queries(self) -> List[Dict[str, Any]]: ... From 4fe8fa82f31d775fcc4699d5d389d419d84ac8cc Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:49:01 +0100 Subject: [PATCH 28/31] . --- pytest_django/fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 890c25ae..a5a84bb0 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -703,7 +703,7 @@ def _assert_num_queries( conn = default_conn with CaptureQueriesContext(conn) as context: - yield _assert_num_queries_context( + yield from _assert_num_queries_context( config=config, context=context, num=num, exact=exact, info=info ) @@ -718,7 +718,7 @@ def _assert_num_queries_all_db( """A recreation of pytest-django's assert_num_queries that works with all databases in settings.Databases.""" with CaptureAllConnectionsQueriesContext() as context: - yield _assert_num_queries_context( + yield from _assert_num_queries_context( config=config, context=context, num=num, exact=exact, info=info ) From f7fff97c3e4551828155afec8707f932a3e0ddbe Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 20:55:52 +0100 Subject: [PATCH 29/31] . --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 914e96cb..2c6ff43e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -108,6 +108,8 @@ skip_covered = true exclude_lines = [ "pragma: no cover", "if TYPE_CHECKING:", + "pass", + "...", ] [tool.ruff] From 894373dbaeb6c988b79837235ac70a2f863c7ae8 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 21:11:59 +0100 Subject: [PATCH 30/31] adds docs --- docs/changelog.rst | 14 ++++++++++ docs/helpers.rst | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 86252fdc..81282dae 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,20 @@ Changelog ========= +v4.11.0 (Not released yet) +-------------------------- + +Compatibility +^^^^^^^^^^^^^ + +* Added fixtures :fixture:`django_assert_num_queries_all_connections` and + :fixture:`django_assert_max_num_queries_all_connections` to check all + your database connections at once. + +Bugfixes +^^^^^^^^ + + v4.10.0 (2025-02-10) -------------------- diff --git a/docs/helpers.rst b/docs/helpers.rst index c9e189dd..d217118b 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -491,6 +491,75 @@ If you use type annotations, you can annotate the fixture like this:: ... +.. fixture:: django_assert_num_queries_all_connections + +``django_assert_num_queries_all_connections`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:function:: django_assert_num_queries_all_connections(num, info=None) + + :param num: expected number of queries + +This fixture allows to check for an expected number of DB queries on all +your database connections. + +If the assertion failed, the executed queries can be shown by using +the verbose command line option. + +It wraps ``django.test.utils.CaptureQueriesContext`` and yields the wrapped +``DjangoAssertNumAllConnectionsQueries`` instance. + +Example usage:: + + def test_queries(django_assert_num_queries_all_connections): + with django_assert_num_queries_all_connections(3) as captured: + Item.objects.using("default").create('foo') + Item.objects.using("logs").create('bar') + Item.objects.using("finance").create('baz') + + assert 'foo' in captured.captured_queries[0]['sql'] + +If you use type annotations, you can annotate the fixture like this:: + + from pytest_django import DjangoAssertNumAllConnectionsQueries + + def test_num_queries( + django_assert_num_queries: DjangoAssertNumAllConnectionsQueries, + ): + ... + + +.. fixture:: django_assert_max_num_queries_all_connections + +``django_assert_max_num_queries_all_connections`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:function:: django_assert_max_num_queries_all_connections(num, info=None) + + :param num: expected maximum number of queries + +This fixture allows to check for an expected maximum number of DB queries on all +your database connections. + +It is a specialized version of :fixture:`django_assert_num_queries_all_connections`. + +Example usage:: + + def test_max_queries(django_assert_max_num_queries_all_connections): + with django_assert_max_num_queries_all_connections(2): + Item.objects.using("logs").create('foo') + Item.objects.using("finance").create('bar') + +If you use type annotations, you can annotate the fixture like this:: + + from pytest_django import DjangoAssertNumAllConnectionsQueries + + def test_max_num_queries( + django_assert_max_num_queries_all_connections: DjangoAssertNumAllConnectionsQueries, + ): + ... + + .. fixture:: django_capture_on_commit_callbacks ``django_capture_on_commit_callbacks`` From aa3d204837720a25120ac04efd7a837ff0430e23 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 10 Feb 2025 21:18:06 +0100 Subject: [PATCH 31/31] . --- docs/helpers.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/helpers.rst b/docs/helpers.rst index d217118b..7f08cb52 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -494,7 +494,7 @@ If you use type annotations, you can annotate the fixture like this:: .. fixture:: django_assert_num_queries_all_connections ``django_assert_num_queries_all_connections`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. py:function:: django_assert_num_queries_all_connections(num, info=None) @@ -532,7 +532,7 @@ If you use type annotations, you can annotate the fixture like this:: .. fixture:: django_assert_max_num_queries_all_connections ``django_assert_max_num_queries_all_connections`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. py:function:: django_assert_max_num_queries_all_connections(num, info=None)