def __init__(self, url): self.url = url # add callbacks to these to learn if we failed to connect # (approximately) or if the ccnnection was unexpectedly lost self.connectionFailed = defer.Deferred() self.connectionLost = defer.Deferred() self._listeners = set() log.info("start read from %s", url) # note: fullGraphReceived isn't guaranteed- the stream could # start with patches self._fullGraphReceived = False self._eventSource = EventSource(url.toPython().encode("utf8")) self._eventSource.protocol.delimiter = "\n" self._eventSource.addEventListener("fullGraph", self._onFullGraph) self._eventSource.addEventListener("patch", self._onPatch) self._eventSource.onerror(self._onError) origSet = self._eventSource.protocol.setFinishedDeferred def sfd(d): origSet(d) d.addCallback(self._onDisconnect) self._eventSource.protocol.setFinishedDeferred = sfd
class PatchSource(object): """wrap EventSource so it emits Patch objects and has an explicit stop method.""" def __init__(self, url): self.url = url # add callbacks to these to learn if we failed to connect # (approximately) or if the ccnnection was unexpectedly lost self.connectionFailed = defer.Deferred() self.connectionLost = defer.Deferred() self._listeners = set() log.info("start read from %s", url) # note: fullGraphReceived isn't guaranteed- the stream could # start with patches self._fullGraphReceived = False self._eventSource = EventSource(url.toPython().encode("utf8")) self._eventSource.protocol.delimiter = "\n" self._eventSource.addEventListener("fullGraph", self._onFullGraph) self._eventSource.addEventListener("patch", self._onPatch) self._eventSource.onerror(self._onError) origSet = self._eventSource.protocol.setFinishedDeferred def sfd(d): origSet(d) d.addCallback(self._onDisconnect) self._eventSource.protocol.setFinishedDeferred = sfd def stats(self): return {"url": self.url, "fullGraphReceived": self._fullGraphReceived} def addPatchListener(self, func): """ func(patch, fullGraph=[true if the patch is the initial fullgraph]) """ self._listeners.add(func) def stop(self): log.info("stop read from %s", self.url) try: self._eventSource.protocol.stopProducing() # needed? except AttributeError: pass self._eventSource = None def _onDisconnect(self, a): log.debug("PatchSource._onDisconnect from %s", self.url) # skip this if we're doing a stop? self.connectionLost.callback(None) def _onError(self, msg): log.debug("PatchSource._onError from %s %r", self.url, msg) if not self._fullGraphReceived: self.connectionFailed.callback(msg) else: self.connectionLost.callback(msg) def _onFullGraph(self, message): try: g = ConjunctiveGraph() g.parse(StringInputSource(message), format="json-ld") p = Patch(addGraph=g) self._sendPatch(p, fullGraph=True) except: log.error(traceback.format_exc()) raise self._fullGraphReceived = True def _onPatch(self, message): try: p = patchFromJson(message) self._sendPatch(p, fullGraph=False) except: log.error(traceback.format_exc()) raise def _sendPatch(self, p, fullGraph): log.debug("PatchSource %s received patch %s (fullGraph=%s)", self.url, p.shortSummary(), fullGraph) for lis in self._listeners: lis(p, fullGraph=fullGraph) def __del__(self): if self._eventSource: raise ValueError