1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
""" Gerrit event stream interface.
Class to listen to the Gerrit event stream and dispatch events.
"""
import json
from events import PatchsetCreatedEvent, \
RefUpdatedEvent, ChangeMergedEvent, CommentAddedEvent, \
ChangeAbandonedEvent, ChangeRestoredEvent, \
DraftPublishedEvent
# Event types
CHANGE_MERGED = "change-merged"
PATCHSET_CREATED = "patchset-created"
DRAFT_PUBLISHED = "draft-published"
COMMENT_ADDED = "comment-added"
CHANGE_ABANDONED = "change-abandoned"
CHANGE_RESTORED = "change-restored"
REF_UPDATED = "ref-updated"
class GerritStreamError(Exception):
''' GerritStreamError is raised when an error occurs while
reading the Gerrit events stream.
'''
class GerritStream(object):
''' Gerrit stream handler.
'''
# Map the event types to class names.
_event_dict = {CHANGE_MERGED: "ChangeMergedEvent",
PATCHSET_CREATED: "PatchsetCreatedEvent",
DRAFT_PUBLISHED: "DraftPublishedEvent",
COMMENT_ADDED: "CommentAddedEvent",
CHANGE_ABANDONED: "ChangeAbandonedEvent",
CHANGE_RESTORED: "ChangeRestoredEvent",
REF_UPDATED: "RefUpdatedEvent"}
def __init__(self):
self.listeners = []
def attach(self, listener):
''' Attach the `listener` to the list of listeners.
Raise GerritStreamError if the listener does not match the
expected signature, or if its event handler is not callable.
'''
if not hasattr(listener, "on_gerrit_event"):
raise GerritStreamError("Listener must have `on_gerrit_event` "
"event handler method")
if not callable(listener.on_gerrit_event):
raise GerritStreamError("`on_gerrit_event` must be callable")
if not listener.on_gerrit_event.func_code.co_argcount == 2:
raise GerritStreamError("`on_gerrit_event` must take 1 arg")
if not listener in self.listeners:
self.listeners.append(listener)
def detach(self, listener):
''' Remove the `listener` from the list of listeners.
'''
if listener in self.listeners:
try:
self.listeners.remove(listener)
except ValueError:
pass
def _get_event(self, json_data):
''' Create a new 'GerritEvent' from the JSON object
described in `json_data`.
Return an instance of one of the GerritEvent subclasses.
Raise GerritStreamError if any error occurs.
'''
event_type = json_data["type"]
if event_type in self._event_dict:
classname = self._event_dict[event_type]
try:
return globals()[classname](json_data)
except KeyError, e:
raise GerritStreamError("Error creating event: %s" % e)
raise GerritStreamError("Unexpected event type `%s`" % event_type)
def _dispatch_event(self, event):
''' Dispatch the `event` to the listeners.
'''
for listener in self.listeners:
listener.on_gerrit_event(event)
def stream(self, inputstream):
''' Listen to the `inputstream` and handle JSON objects.
'''
try:
done = 0
while not done:
line = inputstream.readline()
if line:
data = json.loads(line)
self._dispatch_event(self._get_event(data))
else:
break
except IOError, e:
raise GerritStreamError("Error reading event stream: %s" % e)
except ValueError, e:
raise GerritStreamError("Invalid JSON data in event stream: %s" % e)
|