Comprehensive type stubs for Django framework enabling static type checking with mypy
—
Django's signal system provides decoupled notifications for actions happening throughout the framework, enabling loose coupling between applications through event-driven programming patterns.
# Signal base classes
from django.dispatch import Signal, receiver
# Model signals
from django.db.models.signals import (
pre_init, post_init, pre_save, post_save,
pre_delete, post_delete, m2m_changed,
pre_migrate, post_migrate, class_prepared
)
# Core framework signals
from django.core.signals import (
request_started, request_finished,
got_request_exception, setting_changed
)
# Authentication signals
from django.contrib.auth.signals import (
user_logged_in, user_logged_out, user_login_failed
)
# Database backend signals
from django.db.backends.signals import connection_created
# Test signals
from django.test.signals import setting_changed as test_setting_changedCore signal infrastructure for creating and managing custom signals.
class Signal:
"""
Base signal class for creating custom signals.
Attributes:
receivers: List of connected receiver functions
lock: Threading lock for receiver management
use_caching: Whether to cache receiver lookups
sender_receivers_cache: Cache of receivers for specific senders
"""
receivers: list[Any]
lock: threading.Lock
use_caching: bool
sender_receivers_cache: MutableMapping[Any, Any]
def __init__(self, use_caching: bool = True) -> None: ...
def connect(self, receiver: Callable, sender: object | None = None, weak: bool = True, dispatch_uid: Hashable | None = None) -> None:
"""
Connect receiver function to this signal.
Args:
receiver: Function to call when signal is sent
sender: Specific sender to listen for (None for all)
weak: Whether to use weak references
dispatch_uid: Unique identifier for this connection
"""
def disconnect(self, receiver: Callable | None = None, sender: object | None = None, dispatch_uid: str | None = None) -> bool:
"""
Disconnect receiver from this signal.
Args:
receiver: Function to disconnect
sender: Specific sender to disconnect from
dispatch_uid: Unique identifier of connection to remove
Returns:
Whether a receiver was disconnected
"""
def has_listeners(self, sender: Any = None) -> bool:
"""Check if signal has any listeners for given sender."""
def send(self, sender: Any, **named: Any) -> list[tuple[Callable, str | None]]:
"""
Send signal to all connected receivers.
Args:
sender: Object sending the signal
**named: Additional keyword arguments for receivers
Returns:
List of (receiver, response) tuples
"""
def send_robust(self, sender: Any, **named: Any) -> list[tuple[Callable, Exception | Any]]:
"""
Send signal to all receivers, catching exceptions.
Args:
sender: Object sending the signal
**named: Additional keyword arguments for receivers
Returns:
List of (receiver, response_or_exception) tuples
"""
def receiver(signal: Signal | list[Signal] | tuple[Signal, ...], *, sender: object | None = None, weak: bool = True, dispatch_uid: Hashable | None = None) -> Callable:
"""
Decorator for connecting functions to signals.
Args:
signal: Signal or signals to connect to
sender: Specific sender to listen for
weak: Whether to use weak references
dispatch_uid: Unique identifier for connection
Returns:
Decorator function
"""Signals sent during model lifecycle events.
class ModelSignal(Signal):
"""
Signal subclass for model-related events with enhanced connect/disconnect methods.
"""
def connect(self, receiver: Callable, sender: type[Model] | str | None = None, weak: bool = True, dispatch_uid: str | None = None, apps: Apps | None = None) -> None:
"""
Connect receiver to model signal.
Args:
receiver: Function to call when signal is sent
sender: Model class or app label to listen for
weak: Whether to use weak references
dispatch_uid: Unique identifier for connection
apps: Apps registry for lazy signal connections
"""
def disconnect(self, receiver: Callable | None = None, sender: type[Model] | str | None = None, dispatch_uid: str | None = None, apps: Apps | None = None) -> bool | None:
"""Disconnect receiver from model signal."""
# Model lifecycle signals
pre_init: ModelSignal # Before Model.__init__()
post_init: ModelSignal # After Model.__init__()
pre_save: ModelSignal # Before Model.save()
post_save: ModelSignal # After Model.save()
pre_delete: ModelSignal # Before Model.delete()
post_delete: ModelSignal # After Model.delete()
m2m_changed: ModelSignal # When ManyToManyField changes
class_prepared: Signal # When model class is prepared
# Migration signals
pre_migrate: Signal # Before migration
post_migrate: Signal # After migrationModel Signal Usage Examples:
from django.db.models.signals import post_save
from django.dispatch import receiver
from myapp.models import User
@receiver(post_save, sender=User)
def user_post_save(sender, instance, created, **kwargs):
if created:
# Handle new user creation
send_welcome_email(instance)
else:
# Handle user update
update_search_index(instance)
# Connect without decorator
def my_handler(sender, **kwargs):
pass
post_save.connect(my_handler, sender=User)Signals for core Django framework events.
# Request/response cycle signals
request_started: Signal # When Django starts processing request
request_finished: Signal # When Django finishes processing request
got_request_exception: Signal # When request processing raises exception
# Configuration signals
setting_changed: Signal # When Django setting is changedCore Signal Usage Examples:
from django.core.signals import request_started
from django.dispatch import receiver
@receiver(request_started)
def my_request_started_handler(sender, environ, **kwargs):
# Handle request start
log_request_started(environ)
# Settings change handler
@receiver(setting_changed)
def setting_changed_handler(sender, setting, value, enter, **kwargs):
if setting == 'DEBUG':
configure_logging(value)Signals for user authentication events.
# Authentication events (from django.contrib.auth.signals)
user_logged_in: Signal # When user logs in
user_logged_out: Signal # When user logs out
user_login_failed: Signal # When login attempt failsAuthentication Signal Usage Examples:
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver
@receiver(user_logged_in)
def user_logged_in_handler(sender, request, user, **kwargs):
# Track user login
LoginHistory.objects.create(
user=user,
ip_address=request.META.get('REMOTE_ADDR'),
timestamp=timezone.now()
)Signals for database connection events.
# Database backend events
connection_created: Signal # When database connection is createdCreating and using custom signals for application-specific events.
# Creating custom signals
from django.dispatch import Signal
# Custom signal with specific arguments
order_completed = Signal()
# Signal with documented arguments
payment_processed = Signal() # providing_args=['amount', 'payment_method']Custom Signal Usage Examples:
from django.dispatch import Signal, receiver
# Define custom signal
order_completed = Signal()
# Send signal
def complete_order(order):
# Complete order logic
order.status = 'completed'
order.save()
# Send signal
order_completed.send(
sender=order.__class__,
instance=order,
total=order.total,
user=order.user
)
# Handle custom signal
@receiver(order_completed)
def handle_order_completion(sender, instance, total, user, **kwargs):
# Send confirmation email
send_order_confirmation(user.email, instance)
# Update inventory
update_inventory_for_order(instance)
# Analytics tracking
track_order_completion(user, total)@receiver(post_save, sender=User)
def conditional_handler(sender, instance, created, **kwargs):
# Only handle specific conditions
if created and instance.is_active:
send_activation_email(instance)# Temporary disconnection
from django.db.models.signals import post_save
def bulk_create_users(user_data_list):
# Disconnect signal to avoid sending emails during bulk creation
post_save.disconnect(send_welcome_email, sender=User)
try:
User.objects.bulk_create([
User(**data) for data in user_data_list
])
finally:
# Reconnect signal
post_save.connect(send_welcome_email, sender=User)# Handle exceptions in signal receivers
from django.db.models.signals import post_save
import logging
logger = logging.getLogger(__name__)
@receiver(post_save, sender=User)
def robust_post_save_handler(sender, instance, **kwargs):
try:
# Potentially failing operation
external_api_call(instance)
except Exception as e:
# Log error but don't break the main flow
logger.error(f"Failed to sync user {instance.id}: {e}")from django.db.models.signals import m2m_changed
from django.dispatch import receiver
@receiver(m2m_changed, sender=User.groups.through)
def groups_changed(sender, instance, action, pk_set, **kwargs):
if action == 'post_add':
# Handle groups added to user
for group_id in pk_set:
assign_group_permissions(instance, group_id)
elif action == 'post_remove':
# Handle groups removed from user
for group_id in pk_set:
revoke_group_permissions(instance, group_id)from django.test.utils import override_settings
from django.db.models.signals import post_save
class SignalTestCase(TestCase):
def test_signal_handler(self):
# Track signal calls
signal_calls = []
def test_handler(sender, **kwargs):
signal_calls.append(kwargs)
post_save.connect(test_handler, sender=User)
try:
user = User.objects.create(username='test')
self.assertEqual(len(signal_calls), 1)
self.assertEqual(signal_calls[0]['instance'], user)
finally:
post_save.disconnect(test_handler, sender=User)Install with Tessl CLI
npx tessl i tessl/pypi-django-stubs