﻿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),master,Normal,wontfix,signals,semente+djangoproject@… john+djangoproject@…,Design decision needed,1,0,0,1,0,0