﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
9015	Signal Connection Decorators	zvoase	Brian Rosner	"Usually, signal receivers are defined as functions and then connected to a specific signal via a function call to the signal instance's {{{connect}}} method. The problem is, this registration of the receiver is located outside the receiver's definition. This can cause clutter, it violates DRY, and it is not very Pythonic in style. Several examples of the current usage pattern are included in the [http://docs.djangoproject.com/en/dev/topics/signals/ signal docs], so I don't need to go into too much detail.

I propose a change to the {{{connect}}} method on the {{{django.dispatch.dispatcher.Signal}}} class, which would allow you to decorate a signal receiver function and therefore skip the explicit call to the method. The usage of the new method would look something like this:

{{{
#!python
from django.db.models.signals import pre_save # Just an example of a signal.

@pre_save.connect(sender=MyModel) # Other keyword arguments may be given.
def receiver(sender, instance, *args, **kwargs):
    pass # Do something here.
}}}

----

I've written a patch which does exactly this, preserving backwards compatibility also (although that's not the biggest issue right now; signals are a very recent addition). It works by moving the current {{{Signal.connect}}} method to {{{Signal._connect}}}, replacing it with a small wrapper around the original which, when called, does one of two things:

  * If called with positional arguments (and, optionally, keyword arguments), it behaves like the old {{{Signal.connect}}}, connecting the given receiver function to the signal. This allows it to be used as both a decorator and a registration function.
  * If called with keyword arguments but no positional arguments, it returns a wrapper function, allowing it to decorate the receiver function and include the given keyword arguments (thanks to Python's lexical scoping).

This means it will work like so (with the effects easy to determine from the code):

{{{
#!python
from django.db.models.signals import pre_save

# Decorator with no args.
@pre_save.connect
def recvr1(sender, instance, *args, **kwargs):
    pass # ...

# Registration with only the receiver.
pre_save.connect(recvr1)

# Decorator with keyword args.
@pre_save.connect(sender=MyModel)
def recvr2(sender, instance, *args, **kwargs):
    pass # ...

# Registration with both receiver and keyword args.
pre_save.connect(recvr2, sender=MyModel)
}}}"	New feature	closed	Core (Other)	dev	Normal	wontfix	signals	semente+djangoproject@… john+djangoproject@…	Design decision needed	1	0	0	1	0	0
