def _dispatch(self, event): try: name = event['event'] data = event['data'] payload = json.loads(data, object_hook = Mastodon._Mastodon__json_hooks) except KeyError as err: exception = MastodonMalformedEventError('Missing field', err.args[0], event) self.on_abort(exception) six.raise_from( exception, err ) except ValueError as err: # py2: plain ValueError # py3: json.JSONDecodeError, a subclass of ValueError exception = MastodonMalformedEventError('Bad JSON', data) self.on_abort(exception) six.raise_from( exception, err ) handler_name = 'on_' + name try: handler = getattr(self, handler_name) except AttributeError as err: exception = MastodonMalformedEventError('Bad event type', name) self.on_abort(exception) six.raise_from( exception, err ) else: handler(payload)
def handle_stream(self, lines): """ Handles a stream of events from the Mastodon server. When each event is received, the corresponding .on_[name]() method is called. lines: an iterable of lines of bytes sent by the Mastodon server, as returned by requests.Response.iter_lines(). """ event = {} for raw_line in lines: try: line = raw_line.decode('utf-8') except UnicodeDecodeError as err: six.raise_from( MastodonMalformedEventError("Malformed UTF-8", line), err) if line.startswith(':'): self.handle_heartbeat() elif line == '': # end of event self._dispatch(event) event = {} else: key, value = line.split(': ', 1) # According to the MDN spec, repeating the 'data' key # represents a newline(!) if key in event: event[key] += '\n' + value else: event[key] = value
def handle_stream(self, response): """ Handles a stream of events from the Mastodon server. When each event is received, the corresponding .on_[name]() method is called. response; a requests response object with the open stream for reading. """ event = {} line_buffer = bytearray() for chunk in response.iter_content(chunk_size=1): if chunk: if chunk == b'\n': try: line = line_buffer.decode('utf-8') except UnicodeDecodeError as err: six.raise_from( MastodonMalformedEventError("Malformed UTF-8"), err) if line == '': self._dispatch(event) event = {} else: event = self._parse_line(line, event) line_buffer = bytearray() else: line_buffer.extend(chunk)
def handle_stream(self, response): """ Handles a stream of events from the Mastodon server. When each event is received, the corresponding .on_[name]() method is called. response; a requests response object with the open stream for reading. """ event = {} line_buffer = bytearray() try: for chunk in response.iter_content(chunk_size=1): if chunk: if chunk == b'\n': try: line = line_buffer.decode('utf-8') except UnicodeDecodeError as err: six.raise_from( MastodonMalformedEventError("Malformed UTF-8"), err) if line == '': self._dispatch(event) event = {} else: event = self._parse_line(line, event) line_buffer = bytearray() else: line_buffer.extend(chunk) except requests.exceptions.ChunkedEncodingError as e: # Empirically, we get this exception when the server sends an empty # message (incomplete read) after several hours of running. # This is tantamount to the connection terminating, so call on_close # to allow clients to handle this case. self.on_close()
def on_update(self, status): if self.update_handler != None: self.update_handler(status) try: if self.local_update_handler != None and not "@" in status["account"]["acct"]: self.local_update_handler(status) except Exception as err: six.raise_from( MastodonMalformedEventError('received bad update', status), err )
def _dispatch(self, event): try: name = event['event'] data = event['data'] payload = json.loads(data, object_hook=Mastodon._Mastodon__json_hooks) except KeyError as err: six.raise_from( MastodonMalformedEventError('Missing field', err.args[0], event), err) except ValueError as err: # py2: plain ValueError # py3: json.JSONDecodeError, a subclass of ValueError six.raise_from(MastodonMalformedEventError('Bad JSON', data), err) handler_name = 'on_' + name try: handler = getattr(self, handler_name) except AttributeError as err: six.raise_from(MastodonMalformedEventError('Bad event type', name), err) else: # TODO: allow handlers to return/raise to stop streaming cleanly handler(payload)
def handle_stream(self, response): """ Handles a stream of events from the Mastodon server. When each event is received, the corresponding .on_[name]() method is called. response; a requests response object with the open stream for reading. """ event = {} line_buffer = bytearray() try: for chunk in response.iter_content(chunk_size = 1): if chunk: for chunk_part in chunk: chunk_part = bytearray([chunk_part]) if chunk_part == b'\n': try: line = line_buffer.decode('utf-8') except UnicodeDecodeError as err: exception = MastodonMalformedEventError("Malformed UTF-8") self.on_abort(exception) six.raise_from( exception, err ) if line == '': self._dispatch(event) event = {} else: event = self._parse_line(line, event) line_buffer = bytearray() else: line_buffer.extend(chunk_part) except ChunkedEncodingError as err: exception = MastodonNetworkError("Server ceased communication.") self.on_abort(exception) six.raise_from( exception, err ) except MastodonReadTimeout as err: exception = MastodonReadTimeout("Timed out while reading from server."), self.on_abort(exception) six.raise_from( exception, err )
def _parse_line(self, line, event): if line.startswith(':'): self.handle_heartbeat() else: try: key, value = line.split(': ', 1) except: exception = MastodonMalformedEventError("Malformed event.") self.on_abort(exception) raise exception # According to the MDN spec, repeating the 'data' key # represents a newline(!) if key in event: event[key] += '\n' + value else: event[key] = value return event