aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAljoscha Rittner <[email protected]>2022-06-20 12:23:37 +0200
committerAljoscha Rittner <[email protected]>2022-06-24 17:39:26 +0200
commitaf59a460687864341b544159a714e4de3891ff08 (patch)
tree4141d09b0d0b52e50dfd63d3958023adcec8e87b
parentc9008a1cdcd31ba47c10d7b6f35cc7aceffe81f5 (diff)
downloadmastodon.py-af59a460687864341b544159a714e4de3891ff08.tar.gz
Support of processing unknown events and event names with dots.
#Fixes 234
-rw-r--r--mastodon/streaming.py50
-rw-r--r--tests/test_streaming.py40
2 files changed, 75 insertions, 15 deletions
diff --git a/mastodon/streaming.py b/mastodon/streaming.py
index 214ed1c..ceb61ea 100644
--- a/mastodon/streaming.py
+++ b/mastodon/streaming.py
@@ -45,6 +45,16 @@ class StreamListener(object):
45 contains the resulting conversation dict.""" 45 contains the resulting conversation dict."""
46 pass 46 pass
47 47
48 def on_unknown_event(self, name, unknown_event = None):
49 """An unknown mastodon API event has been received. The name contains the event-name and unknown_event
50 contains the content of the unknown event.
51
52 This function must be implemented, if unknown events should be handled without an error.
53 """
54 exception = MastodonMalformedEventError('Bad event type', name)
55 self.on_abort(exception)
56 raise exception
57
48 def handle_heartbeat(self): 58 def handle_heartbeat(self):
49 """The server has sent us a keep-alive message. This callback may be 59 """The server has sent us a keep-alive message. This callback may be
50 useful to carry out periodic housekeeping tasks, or just to confirm 60 useful to carry out periodic housekeeping tasks, or just to confirm
@@ -56,6 +66,11 @@ class StreamListener(object):
56 Handles a stream of events from the Mastodon server. When each event 66 Handles a stream of events from the Mastodon server. When each event
57 is received, the corresponding .on_[name]() method is called. 67 is received, the corresponding .on_[name]() method is called.
58 68
69 When the Mastodon API changes, the on_unknown_event(name, content)
70 function is called.
71 The default behavior is to throw an error. Define a callback handler
72 to intercept unknown events if needed (and avoid errors)
73
59 response; a requests response object with the open stream for reading. 74 response; a requests response object with the open stream for reading.
60 """ 75 """
61 event = {} 76 event = {}
@@ -137,33 +152,32 @@ class StreamListener(object):
137 exception, 152 exception,
138 err 153 err
139 ) 154 )
140 155 # New mastodon API also supports event names with dots:
141 handler_name = 'on_' + name 156 handler_name = 'on_' + name.replace('.', '_')
142 try: 157 # A generic way to handle unknown events to make legacy code more stable for future changes
143 handler = getattr(self, handler_name) 158 handler = getattr(self, handler_name, self.on_unknown_event)
144 except AttributeError as err: 159 if handler != self.on_unknown_event:
145 exception = MastodonMalformedEventError('Bad event type', name)
146 self.on_abort(exception)
147 six.raise_from(
148 exception,
149 err
150 )
151 else:
152 handler(payload) 160 handler(payload)
161 else:
162 handler(name, payload)
163
153 164
154class CallbackStreamListener(StreamListener): 165class CallbackStreamListener(StreamListener):
155 """ 166 """
156 Simple callback stream handler class. 167 Simple callback stream handler class.
157 Can optionally additionally send local update events to a separate handler. 168 Can optionally additionally send local update events to a separate handler.
169 Define an unknown_event_handler for new Mastodon API events. If not, the
170 listener will raise an error on new, not handled, events from the API.
158 """ 171 """
159 def __init__(self, update_handler = None, local_update_handler = None, delete_handler = None, notification_handler = None, conversation_handler = None): 172 def __init__(self, update_handler = None, local_update_handler = None, delete_handler = None, notification_handler = None, conversation_handler = None, unknown_event_handler = None):
160 super(CallbackStreamListener, self).__init__() 173 super(CallbackStreamListener, self).__init__()
161 self.update_handler = update_handler 174 self.update_handler = update_handler
162 self.local_update_handler = local_update_handler 175 self.local_update_handler = local_update_handler
163 self.delete_handler = delete_handler 176 self.delete_handler = delete_handler
164 self.notification_handler = notification_handler 177 self.notification_handler = notification_handler
165 self.conversation_handler = conversation_handler 178 self.conversation_handler = conversation_handler
166 179 self.unknown_event_handler = unknown_event_handler
180
167 def on_update(self, status): 181 def on_update(self, status):
168 if self.update_handler != None: 182 if self.update_handler != None:
169 self.update_handler(status) 183 self.update_handler(status)
@@ -188,3 +202,11 @@ class CallbackStreamListener(StreamListener):
188 def on_conversation(self, conversation): 202 def on_conversation(self, conversation):
189 if self.conversation_handler != None: 203 if self.conversation_handler != None:
190 self.conversation_handler(conversation) 204 self.conversation_handler(conversation)
205
206 def on_unknown_event(self, name, unknown_event = None):
207 if self.unknown_event_handler != None:
208 self.unknown_event_handler(name, unknown_event)
209 else:
210 exception = MastodonMalformedEventError('Bad event type', name)
211 self.on_abort(exception)
212 raise exception
diff --git a/tests/test_streaming.py b/tests/test_streaming.py
index cddb79a..8912b9c 100644
--- a/tests/test_streaming.py
+++ b/tests/test_streaming.py
@@ -61,6 +61,8 @@ class Listener(StreamListener):
61 self.notifications = [] 61 self.notifications = []
62 self.deletes = [] 62 self.deletes = []
63 self.heartbeats = 0 63 self.heartbeats = 0
64 self.bla_called = False
65 self.do_something_called = False
64 66
65 def on_update(self, status): 67 def on_update(self, status):
66 self.updates.append(status) 68 self.updates.append(status)
@@ -72,6 +74,11 @@ class Listener(StreamListener):
72 self.deletes.append(status_id) 74 self.deletes.append(status_id)
73 75
74 def on_blahblah(self, data): 76 def on_blahblah(self, data):
77 self.bla_called = True
78 pass
79
80 def on_do_something(self, data):
81 self.do_something_called = True
75 pass 82 pass
76 83
77 def handle_heartbeat(self): 84 def handle_heartbeat(self):
@@ -158,6 +165,37 @@ def test_unknown_event():
158 'data: {}', 165 'data: {}',
159 '', 166 '',
160 ]) 167 ])
168 assert listener.bla_called == True
169 assert listener.updates == []
170 assert listener.notifications == []
171 assert listener.deletes == []
172 assert listener.heartbeats == 0
173
174def test_unknown_handled_event():
175 """Be tolerant of new unknown event types, if on_unknown_event is available"""
176 listener = Listener()
177 listener.on_unknown_event = lambda name, payload: None
178
179 listener.handle_stream_([
180 'event: complete.new.event',
181 'data: {"k": "v"}',
182 '',
183 ])
184
185 assert listener.updates == []
186 assert listener.notifications == []
187 assert listener.deletes == []
188 assert listener.heartbeats == 0
189
190def test_dotted_unknown_event():
191 """Be tolerant of new event types with dots in the event-name"""
192 listener = Listener()
193 listener.handle_stream_([
194 'event: do.something',
195 'data: {}',
196 '',
197 ])
198 assert listener.do_something_called == True
161 assert listener.updates == [] 199 assert listener.updates == []
162 assert listener.notifications == [] 200 assert listener.notifications == []
163 assert listener.deletes == [] 201 assert listener.deletes == []
@@ -169,7 +207,7 @@ def test_invalid_event():
169 with pytest.raises(MastodonMalformedEventError): 207 with pytest.raises(MastodonMalformedEventError):
170 listener.handle_stream_([ 208 listener.handle_stream_([
171 'event: whatup', 209 'event: whatup',
172 'data: {}', 210 'data: {"k": "v"}',
173 '', 211 '',
174 ]) 212 ])
175 213
Powered by cgit v1.2.3 (git 2.41.0)