123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- # -*- coding: utf-8 -*-
- """
- Created on Mon Mar 11 18:39:13 2013
- @author Vaclav Petras <wenzeslaus gmail.com>
- """
- from grass.pydispatch import dispatcher
- def _islambda(function):
- """Tests if object is a lambda function.
- Should work on the most of Python implementations where name of lambda
- function is not unique.
-
- >>> mylambda = lambda x: x*x
- >>> _islambda(mylambda)
- True
- >>> _islambda(_islambda)
- False
- """
- return isinstance(function, type(lambda: None)) and function.__name__== (lambda: None).__name__
- class Signal(object):
- """
- >>> signal1 = Signal('signal1')
- >>> def handler1():
- ... print "from handler1"
- >>> signal1.connect(handler1)
- >>> signal2 = Signal('signal2')
- >>> def handler2(text):
- ... print "handler2: %s" % text
- >>> signal2.connect(handler2)
- >>> signal1.emit()
- from handler1
- >>> signal2.emit(text="Hello")
- handler2: Hello
- >>> import sys
- >>> signal2.connect(lambda text:
- ... sys.stdout.write('lambda handler: %s\\n' % text))
- >>> signal2.emit(text="Hi")
- handler2: Hi
- lambda handler: Hi
- >>> def handler3():
- ... print "from handler3"
- >>> signal2.connect(handler3)
- >>> signal2.emit(text="Ciao")
- handler2: Ciao
- lambda handler: Ciao
- from handler3
- >>> signal3 = Signal('signal3')
- >>> signal3.connect(handler3)
- >>> signal1.connect(signal3)
- >>> signal1.emit()
- from handler1
- from handler3
- >>> signal3.disconnect(handler3)
- >>> signal1.emit()
- from handler1
- >>> signal2.disconnect(handler2)
- >>> signal2.disconnect(handler3)
- >>> signal2.emit(text='Hello')
- lambda handler: Hello
- """
- def __init__(self, name):
- self._name = name
- def connect(self, handler, weak=None):
- """
- >>> signal1 = Signal('signal1')
- >>> import sys
- >>> signal1.connect(lambda: sys.stdout.write('will print\\n'))
- >>> signal1.connect(lambda: sys.stdout.write('will print\\n'), weak=False)
- >>> signal1.connect(lambda: sys.stdout.write('will not print'), weak=True)
- >>> signal1.emit()
- will print
- will print
- """
- if weak is None:
- if _islambda(handler):
- weak = False
- else:
- weak = True
- dispatcher.connect(receiver=handler, signal=self, weak=weak)
- def disconnect(self, handler, weak=True):
- """
-
- >>> signal1 = Signal('signal1')
- >>> import sys
- >>> signal1.connect(sys.stdout.write)
- >>> signal1.disconnect(sys.stdout.write)
- >>> signal1.disconnect(sys.stdout.flush) #doctest: +ELLIPSIS
- Traceback (most recent call last):
- DispatcherKeyError: 'No receivers found for signal <__main__.Signal object at 0x...> from sender _Any'
- >>> signal1.disconnect(some_function)
- Traceback (most recent call last):
- NameError: name 'some_function' is not defined
- >>> signal1.emit()
- """
- dispatcher.disconnect(receiver=handler, signal=self, weak=weak)
- # TODO: remove args?, make it work for args?
- def emit(self, *args, **kwargs):
- """
- >>> signal1 = Signal('signal1')
- >>> def mywrite(text):
- ... print text
- >>> signal1.connect(mywrite)
- >>> signal1.emit(text='Hello')
- Hello
- >>> signal1.emit()
- Traceback (most recent call last):
- TypeError: mywrite() takes exactly 1 argument (0 given)
- >>> signal1.emit('Hello')
- Traceback (most recent call last):
- TypeError: send() got multiple values for keyword argument 'signal'
- """
- dispatcher.send(signal=self, *args, **kwargs)
- def __call__(self, *args, **kwargs):
- """Allows to emit signal with function call syntax.
- It allows to handle signal as a function or other callable object.
- So, the signal can be in the list of fuctions or can be connected as
- a handler for another signal.
- However, it is strongly recommended to use emit method for direct
- signal emitting.
- The use of emit method is more explicit than the function call
- and thus it it clear that we are using signal.
- >>> signal1 = Signal('signal1')
- >>> def mywrite(text):
- ... print text
- >>> signal1.connect(mywrite)
- >>> functions = [signal1, lambda text: mywrite(text + '!')]
- >>> for function in functions:
- ... function(text='text')
- text
- text!
- The other reason why the function call should not by used when it is
- possible to use emit method is that this function does ugly hack to
- enable calling as a signal handler. The signal parameter is deleted
- when it is in named keyword arguments. As a consequence, when the
- signal is emitted with the signal parameter (which is a very bad
- name for parameter when using signals), the error is much more readable
- when using emit than function call. Concluding remark is that
- emit behaves more predictable.
- >>> signal1.emit(signal='Hello')
- Traceback (most recent call last):
- TypeError: send() got multiple values for keyword argument 'signal'
- >>> signal1(signal='Hello')
- Traceback (most recent call last):
- TypeError: mywrite() takes exactly 1 argument (0 given)
- """
- if 'signal' in kwargs:
- del kwargs['signal']
- self.emit(*args, **kwargs)
- if __name__ == '__main__':
- import doctest
- doctest.testmod()
|