Code

Ticket #4561: dispatch-smart-signals.patch

File dispatch-smart-signals.patch, 24.8 KB (added by (removed), 7 years ago)

smart signals v1

  • django/dispatch/weaksets.py

    === added file 'django/dispatch/weaksets.py'
     
     1from weakref import WeakKeyDictionary 
     2 
     3class WeakSet(object): 
     4    def __init__(self, initial_vals=None): 
     5        self._map = WeakKeyDictionary() 
     6        if initial_vals is not None: 
     7            self.update(initial_vals) 
     8        self.remove = self._map.__delitem__ 
     9        self.add = self._map.setdefault 
     10 
     11    def update(self, iterable): 
     12        map(self._map.setdefault, iterable) 
     13 
     14    def __len__(self): 
     15        return len(self._map) 
     16 
     17    def __iter__(self): 
     18        return iter(self._map) 
     19 
     20    def discard(self, key): 
     21        self._map.pop(key, None) 
     22 
     23    def __nonzero__(self): 
     24        return bool(self._map) 
     25 
     26    def __repr__(self): 
     27        return "WeakSet(%r)" % self._map.keys() 
     28 
     29    def __str__(self): 
     30        return "WeakSet(%s)" % self._map.keys() 
     31 
     32    def __contains__(self, key): 
     33        return key in self._map 
  • tests/regressiontests/dispatch/tests/test_new_signals.py

    === added file 'tests/regressiontests/dispatch/tests/test_new_signals.py'
     
     1from django import dispatch 
     2from django.dispatch import dispatcher 
     3from unittest import TestCase 
     4 
     5class base(TestCase): 
     6 
     7    def assertIn(self, needle, haystack, msg=None): 
     8        if msg is None: 
     9            msg = "%s wasn't in %s" % (needle, haystack) 
     10        self.assertTrue((needle in haystack), msg=msg) 
     11 
     12class test_signal(base): 
     13 
     14    kls = staticmethod(dispatch.signal) 
     15 
     16    def test_str(self): 
     17        self.assertEqual(str(self.kls("asdf")), "asdf") 
     18 
     19    def test_repr(self): 
     20        o = self.kls("foon") 
     21        self.assertEqual(repr(o), "<signal signal='foon' @#%x>" % id(o)) 
     22 
     23class test_mk_signal(base): 
     24 
     25    func = staticmethod(dispatch.mk_signal) 
     26 
     27    def test_it(self): 
     28        o = self.func("sig1") 
     29        self.assertEqual(o.__doc__, dispatch.signal.__doc__) 
     30        self.assertEqual(o.signal_name, "sig1") 
     31        docstring="mr. super-happy docstring says gimme candy" 
     32        o = self.func("sig2", docstring=docstring) 
     33        self.assertEqual(o.__doc__, docstring) 
     34        self.assertEqual(o.signal_name, 'sig2') 
     35        class dar(dispatch.signal):pass 
     36        self.assertEqual(self.func('sig3', signal_kls=dar).__class__, dar) 
     37 
     38class protect_signals_state_mixin(object): 
     39 
     40    """mixin basically wiping the smart_signals state information clean after backing 
     41    up a copy; upon completion of a fixture, restores the state. 
     42     
     43    Note you cannot rely on existing connections/listeners in any tests using this mixin- 
     44    as said, the state is reset, then restored around the fixture 
     45    """ 
     46 
     47    def setUp(self): 
     48        self._known_sigs = dispatch.smart_signal.known_signals.items() 
     49        self._any_any = dispatch.smart_signal.any_any_count 
     50        self._all_known_senders = dispatch.smart_signal.all_known_senders.items() 
     51        dispatch.smart_signal.known_signals.clear() 
     52        dispatch.smart_signal.any_any_count = 0 
     53        dispatch.smart_signal.all_known_senders.clear() 
     54 
     55    def tearDown(self): 
     56        dispatch.smart_signal.known_signals.clear() 
     57        dispatch.smart_signal.known_signals.update(self._known_sigs) 
     58        dispatch.smart_signal.any_any_count = self._any_any 
     59        dispatch.smart_signal.all_known_senders.clear() 
     60        dispatch.smart_signal.all_known_senders.update(self._all_known_senders) 
     61 
     62class smart_signals_mixin(protect_signals_state_mixin): 
     63 
     64    def mk_signal(self, name="test-signal"): 
     65        class tracking_sig(dispatch.smart_signal): 
     66            def __init__(self, *args): 
     67                dispatch.smart_signal.__init__(self, *args) 
     68                self.enables, self.disables = [], [] 
     69 
     70            def _enable_sender(self, sender, incr): 
     71                self.enables.append((sender, incr)) 
     72 
     73            def _disable_sender(self, sender, incr): 
     74                self.disables.append((sender, incr)) 
     75 
     76        return tracking_sig(name) 
     77 
     78    def mk_kls(self): 
     79        class foo(object): 
     80            def __init__(self, *args): 
     81                self.args = args 
     82 
     83        return foo 
     84 
     85class test_smart_signal(smart_signals_mixin, base): 
     86 
     87    def test_behaviour(self): 
     88        kls, sig = self.mk_kls(), self.mk_signal() 
     89        self.assertEqual([], list(sig.known_senders)) 
     90        obj = kls() 
     91        sig.register_sender(obj) 
     92        self.assertEqual([obj], list(sig.known_senders)) 
     93        del obj 
     94        # assert weakref'd; jython probably won't like this, but oh well 
     95        self.assertEqual([], list(sig.known_senders)) 
     96        self.assertFalse(sig.enables) 
     97        self.assertFalse(sig.disables) 
     98         
     99        # force an Any in to force enables to track that it gets properly enabled, and that 
     100        # _enable_sender is invoked once with the higher incr count 
     101        obj = kls() 
     102        sig.enable_sender(dispatch.Any) 
     103        sig.register_sender(obj) 
     104        self.assertEqual([(obj, 1)], sig.enables) 
     105        obj2 = kls() 
     106        del obj 
     107        sig.enables = [] 
     108        sig.enable_sender(dispatch.Any) 
     109 
     110        # register a new instance; should be a single _enable_sender call, incr=2 due to 
     111        # 2 Any senders register. 
     112         
     113        sig.register_sender(obj2) 
     114        self.assertEqual([(obj2, 2)], sig.enables) 
     115        self.assertFalse(sig.disables) 
     116 
     117class test_Any(smart_signals_mixin, base): 
     118 
     119    def test_register_sender(self): 
     120        self.assertRaises(TypeError, dispatch.Any.register_sender, self.mk_kls()()) 
     121 
     122    def test_enable_disable_sender(self): 
     123        kls = self.mk_kls() 
     124        obj1, obj2 = kls(), kls() 
     125        sig1, sig2 = self.mk_signal("sig1"), self.mk_signal("sig2") 
     126        sig1.register_sender(obj1) 
     127        sig2.register_sender(obj2) 
     128 
     129        self.assertFalse(sig1.enables) 
     130        self.assertFalse(sig2.enables) 
     131 
     132        # first test it's behaviour for a signal=Any, sender=specific 
     133        dispatch.Any.enable_sender(obj1) 
     134        self.assertEqual(sig1.enables, [(obj1, 1)]) 
     135        self.assertFalse(sig2.enables), self.assertFalse(sig2.disables) 
     136        dispatch.Any.disable_sender(obj1) 
     137        self.assertEqual(sig1.enables, [(obj1, 1)]) 
     138        self.assertEqual(sig1.disables, [(obj1, 1)]) 
     139        self.assertFalse(sig2.enables), self.assertFalse(sig2.disables) 
     140 
     141        dispatch.Any.enable_sender(obj2) 
     142        self.assertEqual(sig2.enables, [(obj2, 1)])         
     143        dispatch.Any.disable_sender(obj2) 
     144        self.assertEqual(sig2.disables, [(obj2, 1)])         
     145        sig1.enables, sig1.disables = [], [] 
     146        sig2.enables, sig2.disables = [], [] 
     147 
     148        # now test signal=Any, sender=Any behaviour. 
     149        dispatch.Any.enable_sender(dispatch.Any) 
     150        self.assertEqual(sig1.enables, [(obj1, 1)]) 
     151        self.assertEqual(sig2.enables, [(obj2, 1)]) 
     152        self.assertFalse(sig1.disables), self.assertFalse(sig2.disables) 
     153 
     154        dispatch.Any.enable_sender(dispatch.Any) 
     155        self.assertEqual(sig1.enables, [(obj1, 1)]*2) 
     156        self.assertEqual(sig2.enables, [(obj2, 1)]*2) 
     157 
     158        dispatch.Any.disable_sender(dispatch.Any, 2) 
     159        self.assertEqual(sig1.disables, [(obj1, 2)]) 
     160        self.assertEqual(sig2.disables, [(obj2, 2)]) 
  • tests/regressiontests/dispatch/tests/test_weakset.py

    === added file 'tests/regressiontests/dispatch/tests/test_weakset.py'
     
     1from django.dispatch.weaksets import WeakSet 
     2from unittest import TestCase 
     3 
     4if not hasattr(__builtins__, 'set'): 
     5    from sets import Set as set 
     6 
     7class test_WeakSet(TestCase): 
     8 
     9    def mk_obj(self): 
     10        # can't weakref base object instances, hence subclassing 
     11        class foon(object): 
     12            pass 
     13        return foon() 
     14 
     15    def test_basics(self): 
     16        ws = WeakSet() 
     17        self.assertEqual(len(ws), 0) 
     18        ws.add(self.mk_obj()) 
     19        # should've gone out of scope immediately, since the only referant would be ws, 
     20        # which *better* be weakref'ing it. 
     21        self.assertEqual(len(ws), 0) 
     22        self.assertFalse(ws) 
     23        obj = self.mk_obj() 
     24        ws.add(obj) 
     25        self.assertEqual(len(ws), 1) 
     26        self.assertTrue(ws) 
     27        self.assertEqual(list(ws), [obj]) 
     28        obj2 = self.mk_obj() 
     29        ws.add(obj2) 
     30        self.assertEqual(set(ws), set([obj, obj2])) 
     31         
     32        del obj 
     33        self.assertEqual(list(ws), [obj2]) 
     34        del obj2 
     35        self.assertFalse(ws) 
     36 
     37        obj = self.mk_obj() 
     38        # check that it accepts an iterable 
     39        self.assertEqual(list(WeakSet([obj])), [obj]) 
     40        ws.discard(obj) 
     41        self.assertFalse(ws) 
     42        # no exception 
     43        ws.discard(obj) 
     44        ws.add(obj) 
     45        self.assertTrue(obj) 
     46        ws.remove(obj) 
     47        self.assertFalse(ws) 
     48        # keyerror 
     49        self.assertRaises(KeyError, ws.remove, obj) 
     50 
     51        # finally, update 
     52        objs = [self.mk_obj() for x in xrange(3)] 
     53        ws.update(objs) 
     54        self.assertEqual(set(ws), set(objs)) 
     55 
     56        self.assertTrue(objs[0] in ws) 
     57        self.assertFalse(self in ws) 
  • django/db/models/base.py

    === modified file 'django/db/models/base.py'
     
    6969 
    7070        new_class._prepare() 
    7171 
     72        # now we register the class with the signals it can have turned on; 
     73        signals.pre_init.register_sender(new_class) 
     74        signals.post_init.register_sender(new_class) 
     75        signals.pre_save.register_sender(new_class) 
     76        signals.post_save.register_sender(new_class) 
     77 
    7278        register_models(new_class._meta.app_label, new_class) 
    7379        # Because of the way imports happen (recursively), we may or may not be 
    7480        # the first class for this model to register with the framework. There 
     
    95101        return not self.__eq__(other) 
    96102 
    97103    def __init__(self, *args, **kwargs): 
    98         dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) 
    99104         
    100105        # There is a rather weird disparity here; if kwargs, it's set, then args 
    101106        # overrides it. It should be one or the other; don't duplicate the work  
     
    164169                    pass 
    165170            if kwargs: 
    166171                raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] 
    167         dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self) 
    168172 
    169173    def add_to_class(cls, name, value): 
    170174        if name == 'Admin': 
     
    199203    _prepare = classmethod(_prepare) 
    200204 
    201205    def save(self): 
    202         dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self) 
    203  
    204206        non_pks = [f for f in self._meta.fields if not f.primary_key] 
    205207        cursor = connection.cursor() 
    206208 
     
    252254        transaction.commit_unless_managed() 
    253255 
    254256        # Run any post-save hooks. 
    255         dispatcher.send(signal=signals.post_save, sender=self.__class__, instance=self) 
    256257 
    257258    save.alters_data = True 
    258259 
  • django/db/models/signals.py

    === modified file 'django/db/models/signals.py'
     
    1 class_prepared = object() 
    2  
    3 pre_init= object() 
    4 post_init = object() 
    5  
    6 pre_save = object() 
    7 post_save = object() 
    8  
    9 pre_delete = object() 
    10 post_delete = object() 
    11  
    12 post_syncdb = object() 
     1from django.dispatch import mk_signal, mk_pre_post_signals 
     2from django.dispatch.dispatcher import send 
     3 
     4class_prepared = mk_signal('class_prepared') 
     5 
     6class _init_mixin(object): 
     7    """one of two signals fired when a new model instance is created 
     8 
     9    pre_init is fired when the instance is being created, and is passed two keyword args: 
     10      @param args: positional args passed to __init__ 
     11      @param kwargs: dictionary of keyword args passed to __init__ 
     12    
     13    post_init is fired after the instance is fully created, and is passed a signal keyword arg: 
     14      @param instance: newly created instance 
     15    """ 
     16 
     17    def _pre_replacement(self, func, sender): 
     18        def wrapped(internal_self, *args, **kwargs): 
     19            send(signal=pre_init, sender=internal_self.__class__, args=args, kwargs=kwargs) 
     20            func(internal_self, *args, **kwargs) 
     21        return wrapped 
     22 
     23    def _post_replacement(self, func, sender): 
     24        def wrapped(internal_self, *args, **kwargs): 
     25            func(internal_self, *args, **kwargs) 
     26            send(signal=post_init, sender=internal_self.__class__, instance=internal_self) 
     27        return wrapped 
     28         
     29    def _both_replacement(self, func, sender): 
     30        def wrapped(internal_self, *args, **kwargs): 
     31            send(signal=pre_init, sender=internal_self.__class__, args=args, kwargs=kwargs) 
     32            func(internal_self, *args, **kwargs) 
     33            send(signal=post_init, sender=internal_self.__class__, instance=internal_self) 
     34        return wrapped 
     35 
     36pre_init, post_init = mk_pre_post_signals('__init__', 'pre_init', 'post_init', _init_mixin) 
     37 
     38class _save_mixin(object): 
     39 
     40    def _pre_replacement(self, func, sender): 
     41        def wrapped(internal_self, *args, **kwargs): 
     42            send(signal=pre_save, sender=internal_self.__class__, instance=internal_self) 
     43            func(internal_self, *args, **kwargs) 
     44        wrapped.alters_data = True 
     45        return wrapped 
     46 
     47    def _post_replacement(self, func, sender): 
     48        def wrapped(internal_self, *args, **kwargs): 
     49            func(internal_self, *args, **kwargs) 
     50            send(signal=post_save, sender=internal_self.__class__, instance=internal_self) 
     51        wrapped.alters_data = True 
     52        return wrapped 
     53 
     54    def _both_replacement(self, func, sender): 
     55        def wrapped(internal_self, *args, **kwargs): 
     56            send(signal=pre_save, sender=internal_self.__class__, instance=internal_self) 
     57            func(internal_self, *args, **kwargs) 
     58            send(signal=post_save, sender=internal_self.__class__, instance=internal_self) 
     59        wrapped.alters_data = True 
     60        return wrapped 
     61 
     62pre_save, post_save = mk_pre_post_signals('save', 'pre_save', 'post_save', _save_mixin) 
     63 
     64pre_delete = mk_signal('pre_delete') 
     65post_delete = mk_signal('post_delete') 
     66 
     67post_syncdb = mk_signal('post_syncdb') 
  • django/dispatch/__init__.py

    === modified file 'django/dispatch/__init__.py'
     
    44__author__ = "Patrick K. O'Brien" 
    55__license__ = "BSD-style, see license.txt for details" 
    66 
     7from weakref import WeakKeyDictionary 
     8from weaksets import WeakSet 
     9 
     10class signal(object): 
     11 
     12    def __init__(self, signal_name): 
     13        self.signal_name = signal_name 
     14 
     15    def __str__(self): 
     16        return self.signal_name 
     17 
     18    def __repr__(self): 
     19        return "<signal signal=%r @#%x>" % (self.signal_name, id(self)) 
     20 
     21def mk_signal(signal_name, *args, **kwargs): 
     22    docstring = kwargs.pop("docstring", None) 
     23    signal_kls = kwargs.pop("signal_kls", signal) 
     24    if docstring is not None: 
     25        class signal_kls(signal_kls): 
     26            __doc__ = docstring 
     27    return signal_kls(signal_name, *args, **kwargs) 
     28 
     29 
     30class smart_signal(signal): 
     31 
     32    # tracks known senders (keys), and the 'Any signal' count per sender 
     33    # signal=Any, sender=sender 
     34    all_known_senders = WeakKeyDictionary() 
     35     
     36    # 'Any signal, Any sender' count; tracked for new signal/sender initialization 
     37    any_any_count = 0 
     38 
     39    # tracks known signals, and the # of signal=signal, sender=Any 
     40    # one exemption to this; Any is left out of known_signals. 
     41    known_signals = WeakKeyDictionary() 
     42     
     43    # note per instance, there is a 'known_senders'; tracks the known senders 
     44    # of a specific signal (just that).  This is required to be tracked so 
     45    # that a sender can't be registered twice (will trash the ref cnts) 
     46 
     47    def __init__(self, signal_name): 
     48        signal.__init__(self, signal_name) 
     49        self.known_senders = WeakSet() 
     50        # set the # of sender=any, signal=self count. 
     51        self.known_signals[self] = self.any_any_count 
     52 
     53    def register_sender(self, sender): 
     54        if sender in self.known_senders: 
     55            # already known; can occur for instances that have a shared known_senders. 
     56            return 
     57        self.known_senders.add(sender) 
     58 
     59        # find the incref count from the misc Any currently listening 
     60        count = self.all_known_senders.setdefault(sender, 0) + self.known_signals[self] 
     61        if count: 
     62            self.enable_sender(sender, incr=count) 
     63 
     64    def enable_sender(self, sender, incr=1): 
     65        if sender is Any: 
     66            self.known_signals[self] += 1 
     67            for sender in self.known_senders: 
     68                self._enable_sender(sender, incr) 
     69            return 
     70        self._enable_sender(sender, incr) 
     71 
     72    def disable_sender(self, sender, decr=1): 
     73        if sender is Any: 
     74            self.known_signals[self] -= 1 
     75            # on the offchance a sender goes away when nothing is listening. 
     76            # *probably* not sanely possible 
     77            for sender in tuple(self.known_senders): 
     78                self._disable_sender(sender, decr) 
     79            return 
     80 
     81        self._disable_sender(sender, decr) 
     82 
     83    def _enable_sender(self, sender, incr): 
     84        raise NotImplementedError(self, '_enable_sender') 
     85 
     86    def _disable_sender(self, sender, decr): 
     87        raise NotImplementedError(self, '_disable_sender') 
     88 
     89class Any(smart_signal): 
     90 
     91    """Singleton used to signal either "Any Sender" or "Any Signal" 
     92     
     93    The Any object can be used with connect, disconnect, 
     94    send, or sendExact to signal that the parameter given 
     95    Any should react to all senders/signals, not just 
     96    a particular sender/signal. 
     97    """ 
     98 
     99    def __init__(self): 
     100        signal.__init__(self, "Any") 
     101        self.known_senders = smart_signal.all_known_senders 
     102 
     103    def register_sender(self, sender): 
     104        raise TypeError("you cannot register a sender for Any") 
     105 
     106    def enable_sender(self, sender, incr=1): 
     107        if sender is self: 
     108            for signal in self.known_signals.iterkeys(): 
     109                signal.enable_sender(self, incr=incr) 
     110            smart_signal.any_any_count += incr 
     111        else: 
     112            for signal in self.known_signals.iterkeys(): 
     113                if sender in signal.known_senders: 
     114                    signal.enable_sender(sender, incr=incr) 
     115             
     116    def disable_sender(self, sender, decr=1): 
     117        if sender is self: 
     118            for signal in self.known_signals.iterkeys(): 
     119                signal.disable_sender(self, decr=decr) 
     120            smart_signal.any_any_count -= decr 
     121            assert smart_signal.any_any_count >= 0 
     122        else: 
     123            for signal in self.known_signals.iterkeys(): 
     124                if sender in signal.known_senders: 
     125                    signal.disable_sender(sender, decr=decr) 
     126 
     127Any = Any() 
     128 
     129class _pre_post_signal(smart_signal): 
     130 
     131    def __init__(self, signal_name, method_name, is_pre, known_senders=None): 
     132        smart_signal.__init__(self, signal_name) 
     133        self._method_name = method_name 
     134        if known_senders is not None: 
     135            self.known_senders = known_senders 
     136        self.is_pre = is_pre 
     137 
     138    def _enable_sender(self, sender, incr): 
     139        original_func = func = getattr(sender, self._method_name) 
     140        precount = getattr(original_func, "_listening_pre_refcnt", 0) 
     141        postcount = getattr(original_func, "_listening_post_refcnt", 0) 
     142 
     143        if self.is_pre: 
     144            if postcount: 
     145                if not precount: 
     146                    func = self._both_replacement(func._listening_wrapped, sender) 
     147                    original_func = original_func._listening_wrapped 
     148            elif not precount: 
     149                func = self._pre_replacement(func, sender) 
     150            precount += incr 
     151        else: 
     152            if precount: 
     153                if not postcount: 
     154                    func = self._both_replacement(func._listening_wrapped, sender) 
     155                    original_func = original_func._listening_wrapped 
     156            elif not postcount: 
     157                func = self._post_replacement(func, sender) 
     158            postcount += incr 
     159 
     160        func._listening_pre_refcnt = precount 
     161        func._listening_post_refcnt = postcount 
     162        func._listening_refcnt = precount + postcount 
     163 
     164        if func is not original_func: 
     165            setattr(sender, self._method_name, func) 
     166            func._listening_wrapped = original_func 
     167 
     168    def _disable_sender(self, sender, decr): 
     169        func = original_func = getattr(sender, self._method_name) 
     170 
     171        # shortcut to simplify logic. 
     172        if func._listening_refcnt <= decr: 
     173            setattr(sender, self._method_name, func._listening_wrapped) 
     174            return 
     175         
     176        precount = func._listening_pre_refcnt 
     177        postcount = func._listening_post_refcnt 
     178         
     179        if self.is_pre: 
     180            precount -= decr 
     181            if not precount: 
     182                func = self._post_replacement(func, sender) 
     183                func._listening_refcnt = func._listening_post_refcnt = postcount 
     184            func._listening_pre_refcnt = precount 
     185        else: 
     186            postcount -= decr 
     187            if not postcount: 
     188                func = self._pre_replacement(func, sender) 
     189                func._listening_refcnt = func._listening_pre_refcnt = precount 
     190            func._listening_post_refcnt = postcount 
     191 
     192        if func is not original_func: 
     193            seattr(sender, self._method_name, func) 
     194            func._listening_func = original_func._listening_wrapped 
     195 
     196    def _pre_replacement(self, original_func, sender): 
     197        raise NotImplementedError(self, "_pre_replacement") 
     198 
     199    def _post_replacement(self, original_func, sender): 
     200        raise NotImplementedError(self, "_post_replacement") 
     201 
     202    def _both_replacement(self, original_func, sender): 
     203        raise NotImplementedError(self, "_both_replacement") 
     204 
     205def mk_pre_post_signals(method_name, pre_signal_name, post_signal_name, mixin_kls): 
     206    class mixin(mixin_kls, _pre_post_signal): 
     207        __doc__ = mixin_kls.__doc__ 
     208        pass 
     209    pre = mixin(pre_signal_name, method_name, True) 
     210    pre.__name__ = pre_signal_name 
     211    post = mixin(post_signal_name, method_name, False)# 
     212    post.__name__ = post_signal_name 
     213    return pre, post 
  • django/dispatch/dispatcher.py

    === modified file 'django/dispatch/dispatcher.py'
     
    2626        vs. the original code.) 
    2727""" 
    2828import types, weakref 
    29 from django.dispatch import saferef, robustapply, errors 
     29# Any is imported for backwards compatibility API wise 
     30from django.dispatch import saferef, robustapply, errors, mk_signal, Any 
    3031 
    3132__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" 
    3233__cvsid__ = "$Id: dispatcher.py,v 1.9 2005/09/17 04:55:57 mcfletch Exp $" 
    3334__version__ = "$Revision: 1.9 $"[11:-2] 
    3435 
    35  
    36 class _Parameter: 
    37     """Used to represent default parameter values.""" 
    38     def __repr__(self): 
    39         return self.__class__.__name__ 
    40  
    41 class _Any(_Parameter): 
    42     """Singleton used to signal either "Any Sender" or "Any Signal" 
    43  
    44     The Any object can be used with connect, disconnect, 
    45     send, or sendExact to signal that the parameter given 
    46     Any should react to all senders/signals, not just 
    47     a particular sender/signal. 
    48     """ 
    49 Any = _Any() 
    50  
    51 class _Anonymous(_Parameter): 
     36Anonymous = mk_signal('Anonymous', docstring= 
    5237    """Singleton used to signal "Anonymous Sender" 
    5338 
    5439    The Anonymous object is used to signal that the sender 
     
    6550        in either function then all messages are routed 
    6651        as though there was a single sender (Anonymous) 
    6752        being used everywhere. 
    68     """ 
    69 Anonymous = _Anonymous() 
     53    """) 
    7054 
    7155WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) 
    7256 
     
    137121 
    138122    signals = connections.setdefault(senderkey, {}) 
    139123 
     124    enabler = getattr(signal, 'enable_sender', None) 
     125    if enabler is not None: 
     126        enabler(sender) 
    140127    # Keep track of senders for cleanup. 
    141128    # Is Anonymous something we want to clean up? 
    142129    if sender not in (None, Anonymous, Any): 
     
    203190        ) 
    204191    if weak: receiver = saferef.safeRef(receiver) 
    205192    senderkey = id(sender) 
     193    disabler = getattr(signal, 'disable_sender', None) 
     194    if disabler is not None: 
     195        disabler(sender) 
    206196    try: 
    207197        signals = connections[senderkey] 
    208198        receivers = signals[signal] 
  • tests/regressiontests/dispatch/tests/__init__.py

    === modified file 'tests/regressiontests/dispatch/tests/__init__.py'
     
    55from test_dispatcher import * 
    66from test_robustapply import * 
    77from test_saferef import * 
     8from test_new_signals import * 
     9from test_weakset import *