Skip to content

Commit

Permalink
Provide a means to stop event processing
Browse files Browse the repository at this point in the history
This introduces a mechanism by which event processors may stop event
processing.  This mechanism also allows controlling the return value
of the Processor.process() method.
  • Loading branch information
Kevin L. Mitchell committed Nov 25, 2014
1 parent ce19191 commit fb3ae37
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 5 deletions.
11 changes: 11 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,17 @@ something like the following::
# Process the event
proc.process(ev)

Stop Processing
---------------

It may be necessary for one event processor to stop all event
processing. This could, for instance, be used by a processor that
performs an authorization check if the event fails that check. To
allow this, an event processor may raise the ``evproc.StopProcessing``
exception. The ``StopProcessing`` exception may be initialized with a
``retval`` parameter that will become the return value of
``Processor.process()`` (which normally returns ``None``).

Conclusion
==========

Expand Down
3 changes: 2 additions & 1 deletion evproc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
from evproc.exc import EventException
from evproc.exc import ProcessorReregisterException
from evproc.exc import IncompatibleRequirementsException
from evproc.exc import StopProcessing
from evproc.processor import Processor


__all__ = [
'want', 'requires', 'required_by',
'Event',
'EventException', 'ProcessorReregisterException',
'IncompatibleRequirementsException',
'IncompatibleRequirementsException', 'StopProcessing',
'Processor',
]
19 changes: 19 additions & 0 deletions evproc/exc.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,22 @@ class IncompatibleRequirementsException(EventException):
"""

pass


class StopProcessing(Exception):
"""
An exception that may be raised by an event processor to stop
event processing.
"""

def __init__(self, retval=None):
"""
Initialize a ``StopProcessing`` exception.
:param retval: The return value for ``Processor.process()``.
Defaults to ``None``.
"""

super(StopProcessing, self).__init__('Stop processing')

self.retval = retval
10 changes: 9 additions & 1 deletion evproc/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,16 @@ def process(self, ev):
those processors.
:param ev: The event to process.
:returns: Returns ``None``, or a value passed to a
``StopProcessing`` exception raised by a processor.
"""

# Walk through all the processors and process the event
for proc in self._get_procs(ev):
proc(ev)
try:
proc(ev)
except exc.StopProcessing as ex:
return ex.retval

return None
30 changes: 30 additions & 0 deletions tests/unit/test_exc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2014 Rackspace
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS
# IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language
# governing permissions and limitations under the License.

import unittest

from evproc import exc


class StopProcessingTest(unittest.TestCase):
def test_init_no_args(self):
ex = exc.StopProcessing()

self.assertEqual(ex.retval, None)

def test_init_with_args(self):
ex = exc.StopProcessing('retval')

self.assertEqual(ex.retval, 'retval')
31 changes: 28 additions & 3 deletions tests/unit/test_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,38 @@ def test_load_from(self, mock_ExtensionManager, mock_register):
self.assertEqual(mock_register.call_count, 5)

@mock.patch.object(processor.Processor, '_get_procs')
def test_process(self, mock_get_procs):
nodes = [mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock()]
def test_process_base(self, mock_get_procs):
nodes = [
mock.Mock(),
mock.Mock(),
mock.Mock(),
mock.Mock(),
]
mock_get_procs.return_value = nodes
proc = processor.Processor()

proc.process('ev')
result = proc.process('ev')

self.assertEqual(result, None)
mock_get_procs.assert_called_once_with('ev')
for node in nodes:
node.assert_called_once_with('ev')

@mock.patch.object(processor.Processor, '_get_procs')
def test_process_stop(self, mock_get_procs):
nodes = [
mock.Mock(),
mock.Mock(),
mock.Mock(side_effect=exc.StopProcessing('stop')),
mock.Mock(),
]
mock_get_procs.return_value = nodes
proc = processor.Processor()

result = proc.process('ev')

self.assertEqual(result, 'stop')
mock_get_procs.assert_called_once_with('ev')
for node in nodes[:-1]:
node.assert_called_once_with('ev')
self.assertFalse(nodes[-1].called)

0 comments on commit fb3ae37

Please sign in to comment.