import timeit
from django.dispatch import dispatcher

if False:    
    print "old"
    signal_recv_test = object()    
    signal_noargs_test = object()
    signal_onearg_test = object()
    signal_tenargs_test  = object()
else:
    print "new"    
    from django.dispatch import dispatcher
    signal_recv_test = dispatcher.Signal(providing_args=['arg0'])    
    signal_noargs_test = dispatcher.Signal(providing_args=[])
    signal_onearg_test = dispatcher.Signal(providing_args=['arg0'])
    signal_tenargs_test  = dispatcher.Signal(providing_args=['arg%s' % i for i in range(10)])

def no_op_kwargs(**kwargs):
    pass

def no_op_no_args(sender):
    pass
    
def no_op_onearg(sender, arg0):
    pass

def no_op_tenargs(sender, arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9):
    pass


def call_(i, f):
    for i in range(i):
        f()

receivers = []

x = """
can add params 
multi-reg idempotent
strip extra args to recv based on set -> dict (extensibility later.  worth it?)
error from signal handler breaks all signals (any push-back?)

it's mildly annoying that a signal doesn't know its own name.
using a dict for receivers means signals out-of-order.  never promised, but still to be avoided if not huge diff?
switch all core and contrib  to signal.send rather than dispatcher.send?


original, including pyc generation
0 receivers test 2.16365003586
1 receiver test 18.8934309483 1.24702501297
10 receivers test 101.300621986 4.43934082985

weak dict, weak=True, keithb "faster_signals.diff" Mar 18 2008
0 receivers test 4.87185907364
1 receiver test 11.0454790592 0.341604948044
10 receivers test 52.751529932 4.38265109062

weak dict, weak=False, keithb "faster_signals.diff" Mar 18 2008
0 receivers test 4.82329916954
1 receiver test 11.550688982 0.359184026718
10 receivers test 60.9468040466 4.46081781387

weak_dict, including pyc generation
0 receivers test 1.70886898041
1 receiver test 11.0185530186 1.23874211311
10 receivers test 50.5068600178 4.67185306549

strong_dict, including pyc generation
0 receivers test 1.86286401749
1 receiver test 6.80570101738 1.24223089218
10 receivers test 23.5818219185 4.7426469326

strong_list, including pyc generation
0 receivers test 2.36390304565
1 receiver test 6.74138212204 0.336266994476
10 receivers test 18.7774899006 4.27188205719

strong_list, including pyc generation; no api enforcement
0 receivers test 2.36659097672
1 receiver test 4.90005588531 0.337324142456
10 receivers test 16.6197218895 4.20589017868


-----
separately, comparing performance of argument matching:

with arg matching:
    0 arg bench 6.56428813934
    1 arg bench 7.65555596352
    10 arg bench 19.9225678444
without arg matching:    
    0 arg bench 5.6941409111
    1 arg bench 6.54712295532
    10 arg bench 16.4016561508
"""

prep_timeit = """
from django.dispatch import dispatcher
from __main__ import call_, no_op_kwargs, no_op_no_args
from __main__ import signal_recv_test
from __main__ import signal_noargs_test
from __main__ import signal_onearg_test
from __main__ import signal_tenargs_test
"""

###1) vary on receivers, accepting kwargs 
###2) vary on number of args, not accepting kwargs


####1) vary on receivers, accepting kwargs: 0, 1, 10, 100
print "0 receivers test", timeit.Timer("dispatcher.send(signal_recv_test, arg0=1)", prep_timeit).timeit()
print "0 receivers test direct", timeit.Timer("signal_recv_test.send(None, arg0=1)", prep_timeit).timeit()

dispatcher.connect(no_op_kwargs, signal_recv_test)
print "1 receiver test", timeit.Timer("dispatcher.send(signal_recv_test, arg0=1)", prep_timeit).timeit(), timeit.Timer("no_op_kwargs()", prep_timeit).timeit()
print "1 receiver test direct", timeit.Timer("signal_recv_test.send(None, arg0=1)", prep_timeit).timeit(), timeit.Timer("no_op_kwargs()", prep_timeit).timeit()

for i in range(9):
    def no_op_kwargs(**kwargs):
        pass
    receivers.append(no_op_kwargs)
    dispatcher.connect(no_op_kwargs, signal_recv_test)
print "10 receivers test", timeit.Timer("dispatcher.send(signal_recv_test, arg0=1)", prep_timeit).timeit(), timeit.Timer("call_(10, no_op_kwargs)", prep_timeit).timeit()

#for i in range(90):
#    def no_op_kwargs(**kwargs):
#        pass
#    receivers.append(no_op_kwargs)        
#    dispatcher.connect(no_op_kwargs, signal_recv_test)
#print "100 receivers test", timeit.Timer("dispatcher.send(signal_recv_test, arg0=1)", prep_timeit).timeit(), timeit.Timer("call_(100, no_op_kwargs)", prep_timeit).timeit()
#

####2) vary on number of args, not accepting
dispatcher.connect(no_op_no_args, signal_noargs_test)
dispatcher.connect(no_op_no_args, signal_onearg_test)
dispatcher.connect(no_op_no_args, signal_tenargs_test)
print "0 API enforce bench", timeit.Timer("dispatcher.send(signal_noargs_test)", prep_timeit).timeit(), timeit.Timer("no_op_no_args(None)", prep_timeit).timeit()
print "1 API enforce bench", timeit.Timer("dispatcher.send(signal_onearg_test, arg0=1)", prep_timeit).timeit(), timeit.Timer("no_op_no_args(None)", prep_timeit).timeit()

bench = timeit.Timer("dispatcher.send(signal_tenargs_test, %s)" % ','.join([("arg%s=%s" % (i,i)) for i in range(10)]), prep_timeit).timeit()
lower = timeit.Timer("no_op_no_args(None)", prep_timeit).timeit()
print "10 API enforce bench", bench, lower
#
#dispatcher.disconnect(no_op_no_args, signal_noargs_test)
#dispatcher.disconnect(no_op_no_args, signal_onearg_test)
#dispatcher.disconnect(no_op_no_args, signal_tenargs_test)
#
###3) vary on number of args, accepting
dispatcher.connect(no_op_no_args, signal_noargs_test)
dispatcher.connect(no_op_onearg, signal_onearg_test)
dispatcher.connect(no_op_tenargs, signal_tenargs_test)
print "0 arg bench", timeit.Timer("dispatcher.send(signal_noargs_test)", prep_timeit).timeit()
print "1 arg bench", timeit.Timer("dispatcher.send(signal_onearg_test, arg0=1)", prep_timeit).timeit()
print "10 arg bench", timeit.Timer("dispatcher.send(signal_tenargs_test, %s)" % ','.join([("arg%s=%s" % (i,i)) for i in range(10)]), prep_timeit).timeit()