diff --git a/django/db/__init__.py b/django/db/__init__.py index aa7d02d0f144..dab66c31f785 100644 --- a/django/db/__init__.py +++ b/django/db/__init__.py @@ -1,19 +1,9 @@ from django.core import signals -from django.db.utils import ( - DEFAULT_DB_ALIAS, - DJANGO_VERSION_PICKLE_KEY, - ConnectionHandler, - ConnectionRouter, - DatabaseError, - DataError, - Error, - IntegrityError, - InterfaceError, - InternalError, - NotSupportedError, - OperationalError, - ProgrammingError, -) +from django.db.utils import (DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, + ConnectionHandler, ConnectionRouter, + DatabaseError, DataError, Error, IntegrityError, + InterfaceError, InternalError, NotSupportedError, + OperationalError, ProgrammingError) from django.utils.connection import ConnectionProxy __all__ = [ diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py index 120f2ac6dead..93efaadd6b6c 100644 --- a/django/dispatch/dispatcher.py +++ b/django/dispatch/dispatcher.py @@ -55,6 +55,7 @@ def __init__(self, use_caching=False): # .disconnect() is called and populated on send(). self.sender_receivers_cache = weakref.WeakKeyDictionary() if use_caching else {} self._dead_receivers = False + self._lookup_keys = set() def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): """ @@ -119,10 +120,7 @@ def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): receiver = ref(receiver) weakref.finalize(receiver_object, self._flag_dead_receivers) - # Keep a weakref to sender if possible to ensure associated receivers - # are cleared if it gets garbage collected. This ensures there is no - # id(sender) collisions for distinct senders with non-overlapping - # lifetimes. + # Manage sender weakref as before sender_ref = None if sender is not None: try: @@ -132,9 +130,13 @@ def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): with self.lock: self._clear_dead_receivers() - if not any(r_key == lookup_key for r_key, _, _, _ in self.receivers): + # Use set for fast lookup, reduces connect time from O(n) to O(1) + if lookup_key not in self._lookup_keys: self.receivers.append((lookup_key, receiver, sender_ref, is_async)) - self.sender_receivers_cache.clear() + self._lookup_keys.add(lookup_key) + # Only clear sender_receivers_cache if caching is used + if self.use_caching: + self.sender_receivers_cache.clear() def disconnect(self, receiver=None, sender=None, dispatch_uid=None): """ diff --git a/django/test/utils.py b/django/test/utils.py index 3661010463d5..ded653510545 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -30,6 +30,8 @@ from django.utils.translation import deactivate from django.utils.version import PYPY +_reset_queries_connected = False + try: import jinja2 except ImportError: @@ -737,8 +739,11 @@ def __enter__(self): def __exit__(self, exc_type, exc_value, traceback): self.connection.force_debug_cursor = self.force_debug_cursor - if self.reset_queries_disconnected: + global _reset_queries_connected + # Only (re)connect if we previously disconnected and not already connected + if self.reset_queries_disconnected and not _reset_queries_connected: request_started.connect(reset_queries) + _reset_queries_connected = True if exc_type is not None: return self.final_queries = len(self.connection.queries_log)