[Supervisor-checkins] r853 - in supervisor/trunk: . src/supervisor src/supervisor/tests
Mike Naberezny
mike at maintainable.com
Fri May 22 15:09:03 EDT 2009
Author: Mike Naberezny <mike at maintainable.com>
Date: Fri May 22 15:09:02 2009
New Revision: 853
Log:
- Added new event types that are emitted when a subprocess sends data to
stdout or stderr that would be logged (i.e., data received when not in
the special capture mode used by the PROCESS_COMMUNICATION events).
Event listeners can subscribe to either PROCESS_LOG_STDOUT or
PROCESS_LOG_STDERR individually, or PROCESS_LOG for both.
Modified:
supervisor/trunk/CHANGES.txt
supervisor/trunk/src/supervisor/dispatchers.py
supervisor/trunk/src/supervisor/events.py
supervisor/trunk/src/supervisor/tests/test_dispatchers.py
supervisor/trunk/src/supervisor/tests/test_events.py
Modified: supervisor/trunk/CHANGES.txt
==============================================================================
--- supervisor/trunk/CHANGES.txt (original)
+++ supervisor/trunk/CHANGES.txt Fri May 22 15:09:02 2009
@@ -1,5 +1,11 @@
Next Release
+ - Added new event types that are emitted when a subprocess sends data to
+ stdout or stderr that would be logged (i.e., data received when not in
+ the special capture mode used by the PROCESS_COMMUNICATION events).
+ Event listeners can subscribe to either PROCESS_LOG_STDOUT or
+ PROCESS_LOG_STDERR individually, or PROCESS_LOG for both.
+
- Values for subprocess environment variables specified with environment=
in supervisord.conf can now be optionally quoted, allowing them to
contain commas. Patch by Tim Godfrey.
Modified: supervisor/trunk/src/supervisor/dispatchers.py
==============================================================================
--- supervisor/trunk/src/supervisor/dispatchers.py (original)
+++ supervisor/trunk/src/supervisor/dispatchers.py Fri May 22 15:09:02 2009
@@ -17,6 +17,8 @@
from supervisor.events import notify
from supervisor.events import EventRejectedEvent
+from supervisor.events import ProcessLogStderrEvent
+from supervisor.events import ProcessLogStdoutEvent
from supervisor.states import EventListenerStates
from supervisor import loggers
@@ -151,6 +153,11 @@
config.options.logger.log(
self.mainlog_level, msg, name=config.name,
channel=self.channel, data=data)
+ if self.channel == 'stderr':
+ event = ProcessLogStderrEvent
+ else:
+ event = ProcessLogStdoutEvent
+ notify(event(self.process, self.process.pid, data))
def record_output(self):
if self.capturelog is None:
Modified: supervisor/trunk/src/supervisor/events.py
==============================================================================
--- supervisor/trunk/src/supervisor/events.py (original)
+++ supervisor/trunk/src/supervisor/events.py Fri May 22 15:09:02 2009
@@ -31,6 +31,30 @@
""" Abstract event type """
pass
+class ProcessLogEvent(Event):
+ """ Abstract """
+ def __init__(self, process, pid, data):
+ self.process = process
+ self.pid = pid
+ self.data = data
+
+ def __str__(self):
+ groupname = ''
+ if self.process.group is not None:
+ groupname = self.process.group.config.name
+ return 'processname:%s groupname:%s pid:%s channel:%s\n%s' % (
+ self.process.config.name,
+ groupname,
+ self.pid,
+ self.channel,
+ self.data)
+
+class ProcessLogStdoutEvent(ProcessLogEvent):
+ channel = 'stdout'
+
+class ProcessLogStderrEvent(ProcessLogEvent):
+ channel = 'stderr'
+
class ProcessCommunicationEvent(Event):
""" Abstract """
# event mode tokens
@@ -77,7 +101,7 @@
class SupervisorStoppingEvent(SupervisorStateChangeEvent):
pass
-class EventRejectedEvent: # purposely does not subclass Event
+class EventRejectedEvent: # purposely does not subclass Event
def __init__(self, process, event):
self.process = process
self.event = event
@@ -175,6 +199,9 @@
PROCESS_COMMUNICATION = ProcessCommunicationEvent # abstract
PROCESS_COMMUNICATION_STDOUT = ProcessCommunicationStdoutEvent
PROCESS_COMMUNICATION_STDERR = ProcessCommunicationStderrEvent
+ PROCESS_LOG = ProcessLogEvent
+ PROCESS_LOG_STDOUT = ProcessLogStdoutEvent
+ PROCESS_LOG_STDERR = ProcessLogStderrEvent
REMOTE_COMMUNICATION = RemoteCommunicationEvent
SUPERVISOR_STATE_CHANGE = SupervisorStateChangeEvent # abstract
SUPERVISOR_STATE_CHANGE_RUNNING = SupervisorRunningEvent
Modified: supervisor/trunk/src/supervisor/tests/test_dispatchers.py
==============================================================================
--- supervisor/trunk/src/supervisor/tests/test_dispatchers.py (original)
+++ supervisor/trunk/src/supervisor/tests/test_dispatchers.py Fri May 22 15:09:02 2009
@@ -21,10 +21,12 @@
from supervisor.dispatchers import POutputDispatcher
return POutputDispatcher
- def _makeOne(self, process):
- from supervisor.events import ProcessCommunicationStdoutEvent
- return self._getTargetClass()(process,
- ProcessCommunicationStdoutEvent, 0)
+ def _makeOne(self, process, channel='stdout'):
+ from supervisor import events
+ events = {'stdout': events.ProcessCommunicationStdoutEvent,
+ 'stderr': events.ProcessCommunicationStderrEvent}
+ # dispatcher derives its channel from event class
+ return self._getTargetClass()(process, events[channel], 0)
def test_writable(self):
options = DummyOptions()
@@ -124,7 +126,7 @@
self.assertEqual(dispatcher.childlog.handlers[0].reopened, True)
self.assertEqual(dispatcher.mainlog.handlers[0].reopened, True)
- def test_record_output_non_capturemode(self):
+ def test_record_output_log_non_capturemode(self):
# stdout/stderr goes to the process log and the main log,
# in non-capturemode, the data length doesn't matter
options = DummyOptions()
@@ -141,6 +143,44 @@
"'process1' stdout output:\na")
self.assertEqual(dispatcher.output_buffer, '')
+ def test_record_output_emits_stdout_event(self):
+ options = DummyOptions()
+ config = DummyPConfig(options, 'process1', '/bin/process1')
+ process = DummyProcess(config)
+ dispatcher = self._makeOne(process, 'stdout')
+ dispatcher.output_buffer = 'hello from stdout'
+
+ L = []
+ def doit(event):
+ L.append(event)
+ from supervisor import events
+ events.subscribe(events.EventTypes.PROCESS_LOG_STDOUT, doit)
+ dispatcher.record_output()
+
+ self.assertEqual(len(L), 1)
+ event = L[0]
+ self.assertEqual(event.process, process)
+ self.assertEqual(event.data, 'hello from stdout')
+
+ def test_record_output_emits_stderr_event(self):
+ options = DummyOptions()
+ config = DummyPConfig(options, 'process1', '/bin/process1')
+ process = DummyProcess(config)
+ dispatcher = self._makeOne(process, 'stderr')
+ dispatcher.output_buffer = 'hello from stderr'
+
+ L = []
+ def doit(event):
+ L.append(event)
+ from supervisor import events
+ events.subscribe(events.EventTypes.PROCESS_LOG_STDERR, doit)
+ dispatcher.record_output()
+
+ self.assertEqual(len(L), 1)
+ event = L[0]
+ self.assertEqual(event.process, process)
+ self.assertEqual(event.data, 'hello from stderr')
+
def test_record_output_capturemode_string_longer_than_token(self):
# stdout/stderr goes to the process log and the main log,
# in capturemode, the length of the data needs to be longer
Modified: supervisor/trunk/src/supervisor/tests/test_events.py
==============================================================================
--- supervisor/trunk/src/supervisor/tests/test_events.py (original)
+++ supervisor/trunk/src/supervisor/tests/test_events.py Fri May 22 15:09:02 2009
@@ -65,6 +65,50 @@
class TestEventTypes(unittest.TestCase):
+ def test_ProcessLogEvent_attributes(self):
+ from supervisor.events import ProcessLogEvent
+ inst = ProcessLogEvent(1, 2, 3)
+ self.assertEqual(inst.process, 1)
+ self.assertEqual(inst.pid, 2)
+ self.assertEqual(inst.data, 3)
+
+ def test_ProcessLogEvent_inheritence(self):
+ from supervisor.events import ProcessLogEvent
+ from supervisor.events import Event
+ self.assertTrue(
+ issubclass(ProcessLogEvent, Event)
+ )
+
+ def test_ProcessLogStdoutEvent_attributes(self):
+ from supervisor.events import ProcessLogStdoutEvent
+ inst = ProcessLogStdoutEvent(1, 2, 3)
+ self.assertEqual(inst.process, 1)
+ self.assertEqual(inst.pid, 2)
+ self.assertEqual(inst.data, 3)
+ self.assertEqual(inst.channel, 'stdout')
+
+ def test_ProcessLogStdoutEvent_inheritence(self):
+ from supervisor.events import ProcessLogStdoutEvent
+ from supervisor.events import ProcessLogEvent
+ self.assertTrue(
+ issubclass(ProcessLogStdoutEvent, ProcessLogEvent)
+ )
+
+ def test_ProcessLogStderrEvent_attributes(self):
+ from supervisor.events import ProcessLogStderrEvent
+ inst = ProcessLogStderrEvent(1, 2, 3)
+ self.assertEqual(inst.process, 1)
+ self.assertEqual(inst.pid, 2)
+ self.assertEqual(inst.data, 3)
+ self.assertEqual(inst.channel, 'stderr')
+
+ def test_ProcessLogStderrEvent_inheritence(self):
+ from supervisor.events import ProcessLogStderrEvent
+ from supervisor.events import ProcessLogEvent
+ self.assertTrue(
+ issubclass(ProcessLogStderrEvent, ProcessLogEvent)
+ )
+
def test_ProcessCommunicationEvent_attributes(self):
from supervisor.events import ProcessCommunicationEvent
inst = ProcessCommunicationEvent(1, 2, 3)
@@ -215,7 +259,52 @@
raise AssertionError('headerdata %r could not be deserialized' %
headerdata)
return headers, payload
-
+
+ def test_plog_stdout_event(self):
+ options = DummyOptions()
+ pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
+ process1 = DummyProcess(pconfig1)
+ from supervisor.events import ProcessLogStdoutEvent
+ class DummyGroup:
+ config = pconfig1
+ process1.group = DummyGroup
+ event = ProcessLogStdoutEvent(process1, 1, 'yo')
+ headers, payload = self._deserialize(str(event))
+ self.assertEqual(headers['processname'], 'process1', headers)
+ self.assertEqual(headers['groupname'], 'process1', headers)
+ self.assertEqual(headers['pid'], '1', headers)
+ self.assertEqual(payload, 'yo')
+
+ def test_plog_stderr_event(self):
+ options = DummyOptions()
+ pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
+ process1 = DummyProcess(pconfig1)
+ from supervisor.events import ProcessLogStderrEvent
+ class DummyGroup:
+ config = pconfig1
+ process1.group = DummyGroup
+ event = ProcessLogStderrEvent(process1, 1, 'yo')
+ headers, payload = self._deserialize(str(event))
+ self.assertEqual(headers['processname'], 'process1', headers)
+ self.assertEqual(headers['groupname'], 'process1', headers)
+ self.assertEqual(headers['pid'], '1', headers)
+ self.assertEqual(payload, 'yo')
+
+ def test_pcomm_stdout_event(self):
+ options = DummyOptions()
+ pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
+ process1 = DummyProcess(pconfig1)
+ from supervisor.events import ProcessCommunicationStdoutEvent
+ class DummyGroup:
+ config = pconfig1
+ process1.group = DummyGroup
+ event = ProcessCommunicationStdoutEvent(process1, 1, 'yo')
+ headers, payload = self._deserialize(str(event))
+ self.assertEqual(headers['processname'], 'process1', headers)
+ self.assertEqual(headers['groupname'], 'process1', headers)
+ self.assertEqual(headers['pid'], '1', headers)
+ self.assertEqual(payload, 'yo')
+
def test_pcomm_stdout_event(self):
options = DummyOptions()
pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
More information about the Supervisor-checkins
mailing list