[feat] expose async events in api
[leap_pycommon.git] / src / leap / common / events / __init__.py
1 # -*- coding: utf-8 -*-
2 # __init__.py
3 # Copyright (C) 2013 LEAP
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 """
18 This is an events mechanism that uses a server to allow for emitting events
19 between clients.
20
21 Application components should use the interface available in this file to
22 register callbacks to be executed upon receiving specific events, and to send
23 events to other components.
24
25 To register a callback to be executed when a specific event is emitted, use
26 leap.common.events.register():
27
28 >>> from leap.common.events import register
29 >>> from leap.common.events import catalog
30 >>> register(catalog.CLIENT_UID, lambda sig, content: do_something(content))
31
32 To emit an event, use leap.common.events.emit():
33
34 >>> from leap.common.events import emit
35 >>> from leap.common.events import catalog
36 >>> emit(catalog.CLIENT_UID)
37 """
38 import logging
39 import argparse
40
41 from leap.common.events import client
42 from leap.common.events import txclient
43 from leap.common.events import server
44 from leap.common.events.flags import set_events_enabled
45
46 from leap.common.events import catalog
47
48
49 __all__ = [
50     "register",
51     "unregister",
52     "emit",
53     "catalog",
54     "set_events_enabled"
55 ]
56
57
58 logger = logging.getLogger(__name__)
59
60
61 def register(event, callback, uid=None, replace=False):
62     """
63     Register a callback to be executed when an event is received.
64
65     :param event: The event that triggers the callback.
66     :type event: str
67     :param callback: The callback to be executed.
68     :type callback: callable(event, content)
69     :param uid: The callback uid.
70     :type uid: str
71     :param replace: Wether an eventual callback with same ID should be
72                     replaced.
73     :type replace: bool
74
75     :return: The callback uid.
76     :rtype: str
77
78     :raises CallbackAlreadyRegistered: when there's already a callback
79             identified by the given uid and replace is False.
80     """
81     return client.register(event, callback, uid, replace)
82
83
84 def register_async(event, callback, uid=None, replace=False):
85     return txclient.register(event, callback, uid, replace)
86
87
88 def unregister(event, uid=None):
89     """
90     Unregister callbacks for an event.
91
92     If uid is not None, then only the callback identified by the given uid is
93     removed. Otherwise, all callbacks for the event are removed.
94
95     :param event: The event that triggers the callback.
96     :type event: Event
97     :param uid: The callback uid.
98     :type uid: str
99     """
100     return client.unregister(event, uid)
101
102
103 def unregister_async(event, uid=None):
104     return txclient.unregister(event, uid)
105
106
107 def emit(event, *content):
108     """
109     Send an event.
110
111     :param event: The event to be sent.
112     :type event: Event
113     :param content: The content of the event.
114     :type content: list
115     """
116     return client.emit(event, *content)
117
118
119 def emit_async(event, *content):
120     return txclient.emit(event, *content)
121
122
123 if __name__ == "__main__":
124
125     def _echo(event, *content):
126         print "Received event: (%s, %s)" % (event, content)
127
128     def _parse_args():
129         parser = argparse.ArgumentParser()
130         parser.add_argument(
131             "--debug", "-d", action="store_true",
132             help="print debug information")
133
134         subparsers = parser.add_subparsers(dest="command")
135
136         # server options
137         server_parser = subparsers.add_parser(
138             "server", help="Run an events server.")
139         server_parser.add_argument(
140             "--emit-addr",
141             help="The address in which to listen for events",
142             default=server.EMIT_ADDR)
143         server_parser.add_argument(
144             "--reg-addr",
145             help="The address in which to listen for registration for events.",
146             default=server.REG_ADDR)
147
148         # client options
149         client_parser = subparsers.add_parser(
150             "client", help="Run an events client.")
151         client_parser.add_argument(
152             "--emit-addr",
153             help="The address in which to emit events.",
154             default=server.EMIT_ADDR)
155         client_parser.add_argument(
156             "--reg-addr",
157             help="The address in which to register for events.",
158             default=server.REG_ADDR)
159         group = client_parser.add_mutually_exclusive_group(required=True)
160         group.add_argument('--reg', help="register an event")
161         group.add_argument('--emit', help="send an event")
162         client_parser.add_argument(
163             '--content', help="the content of the event", default=None)
164
165         # txclient options
166         txclient_parser = subparsers.add_parser(
167             "txclient", help="Run an events twisted client.")
168         txclient_parser.add_argument(
169             "--emit-addr",
170             help="The address in which to emit events.",
171             default=server.EMIT_ADDR)
172         txclient_parser.add_argument(
173             "--reg-addr",
174             help="The address in which to register for events.",
175             default=server.REG_ADDR)
176         group = txclient_parser.add_mutually_exclusive_group(required=True)
177         group.add_argument('--reg', help="register an event")
178         group.add_argument('--emit', help="send an event")
179         txclient_parser.add_argument(
180             '--content', help="the content of the event", default=None)
181
182         return parser.parse_args()
183
184     args = _parse_args()
185
186     if args.debug:
187         logging.basicConfig(level=logging.DEBUG)
188
189     if args.command == "server":
190         # run server
191         server.ensure_server(emit_addr=args.emit_addr, reg_addr=args.reg_addr)
192         from twisted.internet import reactor
193         reactor.run()
194     elif args.command == "client":
195         if args.reg:
196             event = getattr(catalog, args.reg)
197             # run client and register to a signal
198             register(event, _echo)
199             # make sure we stop on CTRL+C
200             import signal
201             signal.signal(
202                 signal.SIGINT, lambda sig, frame: client.shutdown())
203             # wait until client thread dies
204             import time
205             while client.EventsClientThread.instance().is_alive():
206                 time.sleep(0.1)
207         if args.emit:
208             # run client and emit a signal
209             event = getattr(catalog, args.emit)
210             emit(event, args.content)
211             client.shutdown()
212     elif args.command == "txclient":
213         from leap.common.events import txclient
214         register = txclient.register
215         emit = txclient.emit
216         if args.reg:
217             event = getattr(catalog, args.reg)
218             # run client and register to a signal
219             register(event, _echo)
220             from twisted.internet import reactor
221             reactor.run()
222         if args.emit:
223             # run client and emit a signal
224             event = getattr(catalog, args.emit)
225             emit(event, args.content)