libpebble2 0.0.29__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. libpebble2/__init__.py +8 -0
  2. libpebble2/communication/__init__.py +389 -0
  3. libpebble2/communication/transports/__init__.py +64 -0
  4. libpebble2/communication/transports/pulse.py +108 -0
  5. libpebble2/communication/transports/qemu/__init__.py +113 -0
  6. libpebble2/communication/transports/qemu/protocol.py +165 -0
  7. libpebble2/communication/transports/serial.py +58 -0
  8. libpebble2/communication/transports/websocket/__init__.py +74 -0
  9. libpebble2/communication/transports/websocket/protocol.py +160 -0
  10. libpebble2/events/__init__.py +93 -0
  11. libpebble2/events/mixin.py +43 -0
  12. libpebble2/events/threaded.py +84 -0
  13. libpebble2/exceptions.py +70 -0
  14. libpebble2/protocol/__init__.py +16 -0
  15. libpebble2/protocol/appglance.py +66 -0
  16. libpebble2/protocol/appmessage.py +88 -0
  17. libpebble2/protocol/apps.py +84 -0
  18. libpebble2/protocol/audio.py +33 -0
  19. libpebble2/protocol/base/__init__.py +234 -0
  20. libpebble2/protocol/base/types.py +635 -0
  21. libpebble2/protocol/blobdb.py +74 -0
  22. libpebble2/protocol/data_logging.py +86 -0
  23. libpebble2/protocol/legacy2.py +157 -0
  24. libpebble2/protocol/logs.py +69 -0
  25. libpebble2/protocol/meta.py +21 -0
  26. libpebble2/protocol/music.py +109 -0
  27. libpebble2/protocol/phone.py +80 -0
  28. libpebble2/protocol/screenshots.py +38 -0
  29. libpebble2/protocol/system.py +241 -0
  30. libpebble2/protocol/timeline.py +94 -0
  31. libpebble2/protocol/transfers.py +152 -0
  32. libpebble2/protocol/voice.py +160 -0
  33. libpebble2/services/__init__.py +1 -0
  34. libpebble2/services/appglances.py +41 -0
  35. libpebble2/services/appmessage.py +211 -0
  36. libpebble2/services/blobdb.py +164 -0
  37. libpebble2/services/data_logging.py +141 -0
  38. libpebble2/services/getbytes.py +85 -0
  39. libpebble2/services/install.py +161 -0
  40. libpebble2/services/notifications.py +97 -0
  41. libpebble2/services/putbytes.py +105 -0
  42. libpebble2/services/screenshot.py +93 -0
  43. libpebble2/services/voice.py +180 -0
  44. libpebble2/util/__init__.py +1 -0
  45. libpebble2/util/bundle.py +203 -0
  46. libpebble2/util/hardware.py +64 -0
  47. libpebble2/util/stm32_crc.py +43 -0
  48. libpebble2/version.py +4 -0
  49. libpebble2-0.0.29.dist-info/METADATA +69 -0
  50. libpebble2-0.0.29.dist-info/RECORD +54 -0
  51. libpebble2-0.0.29.dist-info/WHEEL +5 -0
  52. libpebble2-0.0.29.dist-info/licenses/LICENSE +21 -0
  53. libpebble2-0.0.29.dist-info/top_level.txt +1 -0
  54. libpebble2-0.0.29.dist-info/zip-safe +1 -0
@@ -0,0 +1,165 @@
1
+ from __future__ import absolute_import
2
+ __author__ = 'katharine'
3
+
4
+ from enum import IntEnum
5
+
6
+ from libpebble2.protocol.base import PebblePacket
7
+ from libpebble2.protocol.base.types import *
8
+
9
+ HEADER_SIGNATURE = 0xFEED
10
+ FOOTER_SIGNATURE = 0xBEEF
11
+
12
+
13
+ class QemuSPP(PebblePacket):
14
+ payload = BinaryArray()
15
+
16
+
17
+ class QemuTap(PebblePacket):
18
+ class Axis(IntEnum):
19
+ X = 0
20
+ Y = 1
21
+ Z = 2
22
+
23
+ axis = Uint8()
24
+ direction = Int8()
25
+
26
+
27
+ class QemuBluetoothConnection(PebblePacket):
28
+ connected = Boolean()
29
+
30
+
31
+ class QemuCompass(PebblePacket):
32
+ class Calibration(IntEnum):
33
+ Uncalibrated = 0
34
+ Refining = 1
35
+ Complete = 2
36
+
37
+ heading = Uint32()
38
+ calibrated = Uint8()
39
+
40
+
41
+ class QemuBattery(PebblePacket):
42
+ percent = Uint8()
43
+ charging = Boolean()
44
+
45
+
46
+ class QemuAccelSample(PebblePacket):
47
+ x = Int16()
48
+ y = Int16()
49
+ z = Int16()
50
+
51
+
52
+ class QemuAccel(PebblePacket):
53
+ count = Uint8()
54
+ samples = FixedList(QemuAccelSample, count=count)
55
+
56
+
57
+ class QemuAccelResponse(PebblePacket):
58
+ remaining_space = Uint16()
59
+
60
+
61
+ class QemuVibration(PebblePacket):
62
+ state = Optional(Boolean())
63
+
64
+
65
+ class QemuButton(PebblePacket):
66
+ class Button(IntEnum):
67
+ Back = 1
68
+ Up = 2
69
+ Select = 4
70
+ Down = 8
71
+
72
+ state = Uint8()
73
+
74
+
75
+ class QemuTimeFormat(PebblePacket):
76
+ is_24_hour = Boolean()
77
+
78
+
79
+ class QemuTimelinePeek(PebblePacket):
80
+ enabled = Boolean()
81
+
82
+
83
+ class QemuContentSize(PebblePacket):
84
+ class ContentSize(IntEnum):
85
+ Small = 0
86
+ Medium = 1
87
+ Large = 2
88
+ ExtraLarge = 3
89
+
90
+ size = Uint8()
91
+
92
+ class QemuRebbleTestListRequest(PebblePacket):
93
+ pass
94
+
95
+
96
+ class QemuRebbleTestListResponse(PebblePacket):
97
+ id = Uint16()
98
+ is_last_test = Uint8()
99
+ name_len = Uint8()
100
+ name = BinaryArray(length = name_len)
101
+
102
+
103
+ class QemuRebbleTestRunRequest(PebblePacket):
104
+ id = Uint16()
105
+
106
+
107
+ class QemuRebbleTestComplete(PebblePacket):
108
+ passed = Uint8()
109
+ artifact = Uint32()
110
+
111
+
112
+ class QemuRebbleTestAlive(PebblePacket):
113
+ pass
114
+
115
+
116
+ class QemuRebbleTest(PebblePacket):
117
+ opcode = Uint16()
118
+ payload = Union(opcode, {
119
+ 0x0000: QemuRebbleTestListRequest,
120
+ 0x0001: QemuRebbleTestRunRequest,
121
+ 0x8000: QemuRebbleTestListResponse,
122
+ 0x8001: QemuRebbleTestComplete,
123
+ 0xFFFF: QemuRebbleTestAlive
124
+ })
125
+
126
+
127
+ class QemuPacket(PebblePacket):
128
+ signature = Uint16(default=HEADER_SIGNATURE)
129
+ protocol = Uint16()
130
+ length = Uint16()
131
+ data = Union(protocol, {
132
+ 1: QemuSPP,
133
+ 2: QemuTap,
134
+ 3: QemuBluetoothConnection,
135
+ 4: QemuCompass,
136
+ 5: QemuBattery,
137
+ 6: QemuAccel,
138
+ 8: QemuButton,
139
+ 9: QemuTimeFormat,
140
+ 10: QemuTimelinePeek,
141
+ 11: QemuContentSize,
142
+ 100: QemuRebbleTest,
143
+ }, length=length)
144
+ footer = Uint16(default=FOOTER_SIGNATURE)
145
+
146
+
147
+ class QemuInboundPacket(PebblePacket):
148
+ signature = Uint16(default=HEADER_SIGNATURE)
149
+ protocol = Uint16()
150
+ length = Uint16()
151
+ data = Union(protocol, {
152
+ 1: QemuSPP,
153
+ 6: QemuAccelResponse,
154
+ 7: QemuVibration,
155
+ 100: QemuRebbleTest,
156
+ }, length=length)
157
+ footer = Uint16(default=FOOTER_SIGNATURE)
158
+
159
+
160
+ class QemuRawPacket(PebblePacket):
161
+ signature = Uint16(default=HEADER_SIGNATURE)
162
+ protocol = Uint16()
163
+ length = Uint16()
164
+ data = BinaryArray(length=length)
165
+ footer = Uint16(default=FOOTER_SIGNATURE)
@@ -0,0 +1,58 @@
1
+ from __future__ import absolute_import
2
+ __author__ = 'katharine'
3
+
4
+ import errno
5
+ import serial
6
+ import struct
7
+
8
+ from . import BaseTransport, MessageTargetWatch
9
+ from libpebble2.exceptions import ConnectionError
10
+
11
+
12
+ class SerialTransport(BaseTransport):
13
+ """
14
+ Represents a direct connection to a physical Pebble paired to the computer via Bluetooth serial.
15
+ This transport expects to be given a device file over which it can communicate with the watch via Bluetooth.
16
+
17
+ .. warning::
18
+ Using this transport may cause occasional kernel panics on some versions of OS X.
19
+
20
+ :param device: The path to the device file (on OS X, often of the form ``/dev/cu.PebbleTimeXXXX-SerialPo`` or
21
+ ``/dev/cu.PebbleXXXX-SerialPortSe``).
22
+ :type device: str
23
+ """
24
+ must_initialise = True
25
+
26
+ def __init__(self, device):
27
+ self.device = device
28
+ self.connection = None
29
+
30
+ def connect(self):
31
+ try:
32
+ self.connection = serial.Serial(self.device, 115200)
33
+ except OSError as e:
34
+ if e.errno == errno.EBUSY:
35
+ raise ConnectionError("Could not connect to Pebble.")
36
+ else:
37
+ raise
38
+
39
+ @property
40
+ def connected(self):
41
+ return self.connection is not None and self.connection.isOpen()
42
+
43
+ def read_packet(self):
44
+ try:
45
+ data = self.connection.read(2)
46
+ except serial.SerialException:
47
+ self.connection.close()
48
+ raise ConnectionError("Disconnected from watch.")
49
+ if len(data) < 2:
50
+ raise ConnectionError("Got malformed packet.")
51
+
52
+ length, = struct.unpack('!H', data)
53
+ data += self.connection.read(length + 2)
54
+ return MessageTargetWatch(), data
55
+
56
+ def send_packet(self, message, target=MessageTargetWatch()):
57
+ assert isinstance(target, MessageTargetWatch)
58
+ self.connection.write(message)
@@ -0,0 +1,74 @@
1
+ from __future__ import absolute_import
2
+
3
+ __author__ = 'katharine'
4
+
5
+ import socket
6
+ import struct
7
+ import websocket
8
+
9
+ from .. import BaseTransport, MessageTarget, MessageTargetWatch
10
+ from .protocol import WebSocketRelayToWatch, WebSocketRelayFromWatch, endpoints, from_watch
11
+ from libpebble2.exceptions import ConnectionError, PebbleError
12
+
13
+
14
+ class MessageTargetPhone(MessageTarget):
15
+ """
16
+ Indicates that the message is directed at a connected phone running the Pebble mobile app. For this purpose,
17
+ `pypkjs <https://github.com/pebble/pypkjs>`_ counts as a phone.
18
+ """
19
+ pass
20
+
21
+
22
+ class WebsocketTransport(BaseTransport):
23
+ """
24
+ Represents a connection via WebSocket to a phone running the Pebble mobile app, which is in turn connected
25
+ to a Pebble over Bluetooth.
26
+
27
+ :param url: The WebSocket URL to connect to, in standard format (e.g. ``ws://localhost:9000/``)
28
+ """
29
+ must_initialise = False
30
+
31
+ def __init__(self, url):
32
+ self.url = url
33
+ """:type: str"""
34
+ self.ws = None
35
+ """:type: websocket.WebSocket"""
36
+
37
+ def connect(self):
38
+ try:
39
+ self.ws = websocket.create_connection(self.url)
40
+ except (websocket.WebSocketException, socket.error) as e:
41
+ raise ConnectionError(str(e))
42
+
43
+ @property
44
+ def connected(self):
45
+ return self.ws is not None and self.ws.connected
46
+
47
+ def send_packet(self, message, target=MessageTargetWatch()):
48
+ handlers = {
49
+ MessageTargetWatch: self._send_to_watch,
50
+ MessageTargetPhone: self._send_to_phone,
51
+ }
52
+
53
+ handlers[type(target)](message)
54
+
55
+ def _send_to_watch(self, message):
56
+ self.send_packet(WebSocketRelayToWatch(payload=message), target=MessageTargetPhone())
57
+
58
+ def _send_to_phone(self, message):
59
+ message = struct.pack('B', endpoints[type(message)]) + message.serialise()
60
+ self.ws.send_binary(message)
61
+
62
+ def read_packet(self):
63
+ opcode, message = self.ws.recv_data()
64
+ if opcode == websocket.ABNF.OPCODE_BINARY:
65
+ endpoint, = struct.unpack_from('B', message, 0)
66
+ if from_watch.get(endpoint, None) == WebSocketRelayFromWatch:
67
+ return MessageTargetWatch(), message[1:]
68
+ else:
69
+ packet, length = from_watch[endpoint].parse(message[1:])
70
+ return MessageTargetPhone(), packet
71
+ elif opcode == websocket.ABNF.OPCODE_CLOSE:
72
+ raise ConnectionError("Connection gracefully closed by peer.")
73
+ else:
74
+ raise PebbleError("Got unexpected WebSocket opcode {}".format(opcode))
@@ -0,0 +1,160 @@
1
+ from __future__ import absolute_import
2
+ __author__ = 'katharine'
3
+
4
+ from six import iteritems
5
+
6
+ from enum import IntEnum
7
+
8
+ from libpebble2.protocol.base import PebblePacket
9
+ from libpebble2.protocol.base.types import *
10
+
11
+
12
+ class WebSocketRelayFromWatch(PebblePacket):
13
+ payload = BinaryArray()
14
+
15
+
16
+ class WebSocketRelayToWatch(PebblePacket):
17
+ payload = BinaryArray()
18
+
19
+
20
+ class WebSocketPhoneAppLog(PebblePacket):
21
+ payload = FixedString()
22
+
23
+
24
+ class WebSocketPhoneServerLog(PebblePacket):
25
+ payload = BinaryArray()
26
+
27
+
28
+ class WebSocketInstallBundle(PebblePacket):
29
+ pbw = BinaryArray()
30
+
31
+
32
+ class WebSocketInstallStatus(PebblePacket):
33
+ class StatusCode(IntEnum):
34
+ Success = 0x00
35
+ Failed = 0x01
36
+ status = Uint32()
37
+
38
+
39
+ class WebSocketPhoneInfoRequest(PebblePacket):
40
+ version = Uint8(default=0x00)
41
+
42
+
43
+ class WebSocketInstallPhoneInfoResponse(PebblePacket):
44
+ payload = BinaryArray()
45
+
46
+
47
+ class WebSocketConnectionStatusUpdate(PebblePacket):
48
+ class StatusCode(IntEnum):
49
+ Connected = 0xff
50
+ Disconnected = 0x00
51
+ status = Uint8()
52
+
53
+
54
+ class WebSocketProxyConnectionStatusUpdate(PebblePacket):
55
+ class StatusCode(IntEnum):
56
+ Connected = 0xff
57
+ Disconnected = 0x00
58
+ status = Uint8()
59
+
60
+
61
+ class WebSocketProxyAuthenticationRequest(PebblePacket):
62
+ token = PascalString()
63
+
64
+
65
+ class WebSocketProxyAuthenticationResponse(PebblePacket):
66
+ class StatusCode(IntEnum):
67
+ Success = 0x00
68
+ Failed = 0x01
69
+ status = Uint8()
70
+
71
+
72
+ class AppConfigSetup(PebblePacket):
73
+ pass
74
+
75
+
76
+ class AppConfigResponse(PebblePacket):
77
+ length = Uint32()
78
+ data = FixedString(length=length)
79
+
80
+
81
+ class AppConfigCancelled(PebblePacket):
82
+ pass
83
+
84
+
85
+ class AppConfigURL(PebblePacket):
86
+ length = Uint32()
87
+ data = FixedString(length=length)
88
+
89
+
90
+ class WebSocketPhonesimAppConfig(PebblePacket):
91
+ command = Uint8()
92
+ config = Union(command, {
93
+ 0x01: AppConfigSetup,
94
+ 0x02: AppConfigResponse,
95
+ 0x03: AppConfigCancelled,
96
+ })
97
+
98
+
99
+ class WebSocketPhonesimConfigResponse(PebblePacket):
100
+ command = Uint8()
101
+
102
+ config = Union(command, {
103
+ 0x01: AppConfigURL
104
+ })
105
+
106
+
107
+ class WebSocketRelayQemu(PebblePacket):
108
+ protocol = Uint8()
109
+ data = BinaryArray()
110
+
111
+
112
+ class InsertPin(PebblePacket):
113
+ json = FixedString()
114
+
115
+
116
+ class DeletePin(PebblePacket):
117
+ uuid = FixedString(36)
118
+
119
+
120
+ class WebSocketTimelinePin(PebblePacket):
121
+ command = Uint8()
122
+ data = Union(command, {
123
+ 0x01: InsertPin,
124
+ 0x02: DeletePin
125
+ })
126
+
127
+
128
+ class WebSocketTimelineResponse(PebblePacket):
129
+ class Status(IntEnum):
130
+ Succeeded = 0x00
131
+ Failed = 0x01
132
+
133
+ status = Uint8(enum=Status)
134
+
135
+ to_watch = {
136
+ 0x01: WebSocketRelayToWatch,
137
+ 0x04: WebSocketInstallBundle,
138
+ 0x06: WebSocketPhoneInfoRequest,
139
+ 0x09: WebSocketProxyAuthenticationRequest,
140
+ 0x0a: WebSocketPhonesimAppConfig,
141
+ 0x0b: WebSocketRelayQemu,
142
+ 0x0c: WebSocketTimelinePin
143
+ }
144
+
145
+ from_watch = {
146
+ 0x00: WebSocketRelayFromWatch,
147
+ 0x01: WebSocketRelayToWatch,
148
+ 0x02: WebSocketPhoneAppLog,
149
+ 0x03: WebSocketPhoneServerLog,
150
+ 0x05: WebSocketInstallStatus,
151
+ 0x06: WebSocketPhoneInfoRequest,
152
+ 0x07: WebSocketConnectionStatusUpdate,
153
+ 0x08: WebSocketProxyConnectionStatusUpdate,
154
+ 0x09: WebSocketProxyAuthenticationResponse,
155
+ 0x0a: WebSocketPhonesimConfigResponse,
156
+ 0x0c: WebSocketTimelineResponse,
157
+ }
158
+
159
+ endpoints = {v: k for k, v in iteritems(to_watch)}
160
+ endpoints.update({v: k for k, v in iteritems(from_watch)})
@@ -0,0 +1,93 @@
1
+ from __future__ import absolute_import
2
+ __author__ = 'katharine'
3
+
4
+ from six import with_metaclass
5
+
6
+ from abc import ABCMeta, abstractmethod
7
+
8
+
9
+ class BaseEventHandler(with_metaclass(ABCMeta)):
10
+ """
11
+ An event handler, used throughout libpebble2 to indicate that something happened. These should ordinarily not need
12
+ to be directly invoked by a client of libpebble2.
13
+ """
14
+ @abstractmethod
15
+ def register_handler(self, event, handler):
16
+ """
17
+ Register a handler for an event.
18
+
19
+ :param event: The event to be handled. This can be any object, as long as it's hashable.
20
+ :param handler: A callback function to be called when the event is triggered. The arguments are dependent
21
+ on the event.
22
+ :return: A handle that can be passed to :meth:`unregister_handler` to remove the registration.
23
+ """
24
+ pass
25
+
26
+ @abstractmethod
27
+ def unregister_handler(self, handle):
28
+ """
29
+ Remove a handler for an event using a handle returned by :meth:`register_handler`.
30
+
31
+ :param handle: The handle for the registration to remove.
32
+ """
33
+ pass
34
+
35
+ @abstractmethod
36
+ def wait_for_event(self, event, timeout=10):
37
+ """
38
+ A blocking wait for an event to be fired.
39
+
40
+ :param event: The event to wait on.
41
+ :param timeout: How long to wait before raising :exc:`.TimeoutError`
42
+ :return: The arguments that were passed to :meth:`broadcast_event`.
43
+ """
44
+ pass
45
+
46
+ @abstractmethod
47
+ def queue_events(self, event):
48
+ """
49
+ Returns a :class:`BaseEventQueue` from which events can be read as they arrive, even if the arrive faster
50
+ than they are removed.
51
+
52
+ :param event: The events to add to the queue.
53
+ :return: An event queue.
54
+ :rtype: BaseEventQueue
55
+ """
56
+ pass
57
+
58
+ @abstractmethod
59
+ def broadcast_event(self, event, *args):
60
+ """
61
+ Broadcasts an event to all subscribers for that event, as added by :meth:`register_handler`
62
+ :meth:`wait_for_event` and :meth:`queue_events`. All arguments after `event` are passed on to the listeners.
63
+
64
+ :param event: The event to broadcast.
65
+ :param args: Any arguments to pass on.
66
+ """
67
+ pass
68
+
69
+
70
+ class BaseEventQueue(with_metaclass(ABCMeta)):
71
+ @abstractmethod
72
+ def close(self):
73
+ """
74
+ Stop adding events to this queue. It is illegal to call :meth:`get` or iterate over this queue after calling
75
+ :meth:`close`.
76
+ """
77
+ pass
78
+
79
+ @abstractmethod
80
+ def get(self, timeout=10):
81
+ """
82
+ Get the next event in the queue. Blocks until an item is available.
83
+ """
84
+ pass
85
+
86
+ @abstractmethod
87
+ def __iter__(self):
88
+ """
89
+ Iterate over events in the queue. Blocks if no more items are available.
90
+ """
91
+ pass
92
+
93
+
@@ -0,0 +1,43 @@
1
+ from __future__ import absolute_import
2
+ __author__ = 'katharine'
3
+
4
+ from libpebble2.events.threaded import ThreadedEventHandler
5
+
6
+
7
+ class EventSourceMixin(object):
8
+ """
9
+ A convenient mixin to save on repeatedly exposing generic event handler functionality.
10
+ """
11
+ def __init__(self):
12
+ self.__handler = ThreadedEventHandler()
13
+
14
+ def register_handler(self, event, handler):
15
+ """
16
+ Registers a handler to be triggered by an event
17
+
18
+ :param event: The event to handle
19
+ :param handler: The handler callable.
20
+ :return: A handle that can be used to unregister the handler.
21
+ """
22
+ return self.__handler.register_handler(event, handler)
23
+
24
+ def unregister_handler(self, handle):
25
+ """
26
+ Unregisters an event handler.
27
+
28
+ :param handle: The handle returned from :meth:`register_handler`
29
+ """
30
+ self.__handler.unregister_handler(handle)
31
+
32
+ def wait_for_event(self, event, timeout=10):
33
+ """
34
+ Block waiting for the given event. Returns the event params.
35
+
36
+ :param event: The event to handle.
37
+ :return: The event params.
38
+ :param timeout: The maximum time to wait before raising :exc:`.TimeoutError`.
39
+ """
40
+ return self.__handler.wait_for_event(event, timeout=timeout)
41
+
42
+ def _broadcast_event(self, event, *args):
43
+ return self.__handler.broadcast_event(event, *args)
@@ -0,0 +1,84 @@
1
+ from __future__ import absolute_import
2
+
3
+ __author__ = 'katharine'
4
+
5
+ import threading
6
+ from six.moves import queue
7
+
8
+ from . import BaseEventHandler, BaseEventQueue
9
+ from libpebble2.exceptions import TimeoutError
10
+
11
+
12
+ class ThreadedEventHandler(BaseEventHandler):
13
+ """
14
+ A threaded implementation of :class:`.BaseEventHandler`.
15
+ """
16
+ def __init__(self):
17
+ self._handlers = {}
18
+ self._handle_map = {}
19
+ self._counter = 0
20
+ self._handler_lock = threading.RLock()
21
+
22
+ def register_handler(self, event, handler):
23
+ with self._handler_lock:
24
+ self._counter += 1
25
+ self._handlers.setdefault(event, {})[self._counter] = handler
26
+ self._handle_map[self._counter] = event
27
+ return self._counter
28
+
29
+ def unregister_handler(self, handle):
30
+ with self._handler_lock:
31
+ if handle not in self._handle_map:
32
+ return
33
+ del self._handlers[self._handle_map[handle]][handle]
34
+ del self._handle_map[handle]
35
+
36
+ def wait_for_event(self, event, timeout=10):
37
+ return _BlockingEventWait(self, event).wait(timeout=timeout)
38
+
39
+ def queue_events(self, event):
40
+ return _QueuedEventWait(self, event)
41
+
42
+ def broadcast_event(self, event, *args):
43
+ for handler in list(self._handlers.get(event, {}).values()):
44
+ handler(*args)
45
+
46
+
47
+ class _BlockingEventWait(object):
48
+ def __init__(self, events, event):
49
+ self.block = threading.Event()
50
+ self.event_handler = events
51
+ self.result = None
52
+ self.handle = self.event_handler.register_handler(event, self.handle_result)
53
+
54
+ def handle_result(self, *args):
55
+ self.result, = args
56
+ self.event_handler.unregister_handler(self.handle)
57
+ self.block.set()
58
+
59
+ def wait(self, timeout=10):
60
+ if not self.block.wait(timeout=timeout):
61
+ raise TimeoutError()
62
+ return self.result
63
+
64
+
65
+ class _QueuedEventWait(BaseEventQueue):
66
+ def __init__(self, events, event):
67
+ self.queue = queue.Queue()
68
+ self.event_handler = events
69
+ self.handle = self.event_handler.register_handler(event, self._handle_event)
70
+
71
+ def _handle_event(self, arg):
72
+ self.queue.put(arg)
73
+
74
+ def close(self):
75
+ self.event_handler.unregister_handler(self.handle)
76
+
77
+ def get(self, timeout=10):
78
+ try:
79
+ return self.queue.get(timeout=timeout)
80
+ except queue.Empty:
81
+ raise TimeoutError()
82
+
83
+ def __iter__(self):
84
+ yield self.get()