hooks: run() return value controls processing of entry data.

Instead of using in-band signaling with the entry_data dict.
I don't know why I didn't think of this in the first place.
This commit is contained in:
Brett Smith 2017-12-19 10:18:04 -05:00
parent fa35a70f9f
commit 18eebbc0ed
4 changed files with 23 additions and 11 deletions

View file

@ -51,7 +51,13 @@ Hooks make arbitrary transformations to entry data dicts. Every entry data dict
Initializes the hook with the user's configuration. Initializes the hook with the user's configuration.
``run(entry_data)`` ``run(entry_data)``
This method makes the hook's transformations to the entry data dict, if any. If this method sets ``entry_data['_hook_cancel']`` to a truthy value, that entry will not be output. This method can make arbitrary transformations to the entry data, or filter it so it isn't output.
If this method returns ``None``, processing the entry data continues normally. Most hooks should do this, and just transform entry data in place.
If this method returns ``False``, processing the entry data stops immediately. The entry will not appear in the program output.
If this method returns any other value, the program replaces the entry data with the return value, and continues processing.
Templates Templates
~~~~~~~~~ ~~~~~~~~~
@ -86,7 +92,12 @@ At a high level, import2ledger handles each input file this way::
input_file.seek(0) input_file.seek(0)
for entry_data in importer_class(input_file): for entry_data in importer_class(input_file):
for hook in all_hooks: for hook in all_hooks:
hook.run(entry_data) hook_return = hook.run(entry_data)
if hook_return is False:
break
elif hook_return is not None:
entry_data = hook_return
else:
if entry_data: if entry_data:
template.render(entry_data) template.render(entry_data)

View file

@ -49,13 +49,15 @@ class FileImporter:
default_date = self.config.get_default_date() default_date = self.config.get_default_date()
in_file.seek(0) in_file.seek(0)
for entry_data in importer(in_file): for entry_data in importer(in_file):
entry_data['_hook_cancel'] = False
for hook in self.hooks: for hook in self.hooks:
hook.run(entry_data) hook_retval = hook.run(entry_data)
if entry_data['_hook_cancel']: if hook_retval is None:
pass
elif hook_retval is False:
break break
else: else:
del entry_data['_hook_cancel'] entry_data = hook_retval
else:
render_vars = collections.ChainMap(entry_data, source_vars) render_vars = collections.ChainMap(entry_data, source_vars)
print(template.render(render_vars), file=out_file, end='') print(template.render(render_vars), file=out_file, end='')

View file

@ -9,4 +9,4 @@ class FilterByDateHook:
pass pass
else: else:
if not self.config.date_in_want_range(date): if not self.config.date_in_want_range(date):
entry_data['_hook_cancel'] = True return False

View file

@ -57,8 +57,7 @@ class DateRangeConfig:
def test_filter_by_date(entry_date, start_date, end_date, allowed): def test_filter_by_date(entry_date, start_date, end_date, allowed):
entry_data = {'date': entry_date} entry_data = {'date': entry_date}
hook = filter_by_date.FilterByDateHook(DateRangeConfig(start_date, end_date)) hook = filter_by_date.FilterByDateHook(DateRangeConfig(start_date, end_date))
hook.run(entry_data) assert hook.run(entry_data) is (None if allowed else False)
assert entry_data.get('_hook_cancel', False) == (not allowed)
class DefaultDateConfig: class DefaultDateConfig:
ONE_DAY = datetime.timedelta(days=1) ONE_DAY = datetime.timedelta(days=1)