added basic error stream support

This commit is contained in:
Michael Stephens 2010-06-30 14:13:31 -04:00
parent c5770668e0
commit 54a99d4d05
4 changed files with 60 additions and 13 deletions

View File

@ -4,20 +4,28 @@
import filters, emitters, sources, utils import filters, emitters, sources, utils
class Recipe(filters.Filter):
def __init__(self, *filter_args): class Recipe(object):
def __init__(self, *filter_args, **kwargs):
self._filter_args = filter_args self._filter_args = filter_args
self.rejected = [] self.rejected = []
def get_filters(self): self.error_stream = kwargs.get('error_stream')
if self.error_stream and not isinstance(self.error_stream, Recipe):
if isinstance(self.error_stream, filters.Filter):
self.error_stream = Recipe(self.error_stream)
elif hasattr(self.error_stream, '__iter__'):
self.error_stream = Recipe(*self.error_stream)
else:
raise ValueError('error_stream must be either a filter'
' or an iterable of filters')
def get_filters(self):
filters = [] filters = []
for filter_ in self._filter_args: for filter_ in self._filter_args:
# check to see if this is a filter or a recipe # check to see if this is a filter or a recipe
if hasattr(filter_, 'get_filters'): if hasattr(filter_, 'get_filters'):
# load filters from child recipe # load filters from child recipe
filters.extend(filter_.get_filters()) filters.extend(filter_.get_filters())
@ -26,11 +34,11 @@ class Recipe(filters.Filter):
return filters return filters
def reject_record(self): def reject_record(self, record, message):
self.rejected.append((record, message)) if self.error_stream:
self.error_stream.run([record])
def run(self, source): def run(self, source):
# load filters # load filters
filters = self.get_filters() filters = self.get_filters()
@ -50,6 +58,7 @@ class Recipe(filters.Filter):
except AttributeError: except AttributeError:
pass # don't care if there isn't a done method pass # don't care if there isn't a done method
def run_recipe(source, *filter_args): def run_recipe(source, *filter_args):
""" Process data, taking it from a source and applying any number of filters """ Process data, taking it from a source and applying any number of filters
""" """

View File

@ -38,9 +38,12 @@ class Filter(object):
def attach(self, source, recipe=None): def attach(self, source, recipe=None):
self._recipe = recipe self._recipe = recipe
for record in source: for record in source:
result = self.process_record(record) try:
if result is not None: result = self.process_record(record)
yield result if result is not None:
yield result
except Exception as e:
self.reject_record(record, unicode(e))
class YieldFilter(Filter): class YieldFilter(Filter):
@ -54,8 +57,11 @@ class YieldFilter(Filter):
def attach(self, source, recipe=None): def attach(self, source, recipe=None):
self._recipe = recipe self._recipe = recipe
for record in source: for record in source:
for result in self.process_record(record): try:
yield result for result in self.process_record(record):
yield result
except Exception as e:
self.reject_record(record, unicode(e))
class FieldFilter(Filter): class FieldFilter(Filter):

View File

@ -2,10 +2,12 @@ import unittest
from saucebrush.tests.filters import FilterTestCase from saucebrush.tests.filters import FilterTestCase
from saucebrush.tests.sources import SourceTestCase from saucebrush.tests.sources import SourceTestCase
from saucebrush.tests.emitters import EmitterTestCase from saucebrush.tests.emitters import EmitterTestCase
from saucebrush.tests.recipes import RecipeTestCase
filter_suite = unittest.TestLoader().loadTestsFromTestCase(FilterTestCase) filter_suite = unittest.TestLoader().loadTestsFromTestCase(FilterTestCase)
source_suite = unittest.TestLoader().loadTestsFromTestCase(SourceTestCase) source_suite = unittest.TestLoader().loadTestsFromTestCase(SourceTestCase)
emitter_suite = unittest.TestLoader().loadTestsFromTestCase(EmitterTestCase) emitter_suite = unittest.TestLoader().loadTestsFromTestCase(EmitterTestCase)
recipe_suite = unittest.TestLoader().loadTestsFromTestCase(RecipeTestCase)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -0,0 +1,30 @@
import doctest
import unittest
from saucebrush import Recipe
from saucebrush.filters import Filter
class Raiser(Filter):
def process_record(self, record):
raise Exception("bad record")
class Saver(Filter):
def __init__(self):
self.saved = []
def process_record(self, record):
self.saved.append(record)
return record
class RecipeTestCase(unittest.TestCase):
def test_error_stream(self):
saver = Saver()
recipe = Recipe(Raiser(), error_stream=saver)
recipe.run([{'a': 1}, {'b': 2}])
self.assertEqual(saver.saved, [{'a': 1}, {'b': 2}])
if __name__ == '__main__':
unittest.main()