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:
parent
fa35a70f9f
commit
18eebbc0ed
4 changed files with 23 additions and 11 deletions
19
CODE.rst
19
CODE.rst
|
@ -51,7 +51,13 @@ Hooks make arbitrary transformations to entry data dicts. Every entry data dict
|
|||
Initializes the hook with the user's configuration.
|
||||
|
||||
``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
|
||||
~~~~~~~~~
|
||||
|
@ -86,9 +92,14 @@ At a high level, import2ledger handles each input file this way::
|
|||
input_file.seek(0)
|
||||
for entry_data in importer_class(input_file):
|
||||
for hook in all_hooks:
|
||||
hook.run(entry_data)
|
||||
if entry_data:
|
||||
template.render(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:
|
||||
template.render(entry_data)
|
||||
|
||||
Note in particular that multiple importers can handle the same input file. This helps support inputs like Patreon's earnings CSV, where completely different transactions are generated from the same source.
|
||||
|
||||
|
|
|
@ -49,13 +49,15 @@ class FileImporter:
|
|||
default_date = self.config.get_default_date()
|
||||
in_file.seek(0)
|
||||
for entry_data in importer(in_file):
|
||||
entry_data['_hook_cancel'] = False
|
||||
for hook in self.hooks:
|
||||
hook.run(entry_data)
|
||||
if entry_data['_hook_cancel']:
|
||||
hook_retval = hook.run(entry_data)
|
||||
if hook_retval is None:
|
||||
pass
|
||||
elif hook_retval is False:
|
||||
break
|
||||
else:
|
||||
entry_data = hook_retval
|
||||
else:
|
||||
del entry_data['_hook_cancel']
|
||||
render_vars = collections.ChainMap(entry_data, source_vars)
|
||||
print(template.render(render_vars), file=out_file, end='')
|
||||
|
||||
|
|
|
@ -9,4 +9,4 @@ class FilterByDateHook:
|
|||
pass
|
||||
else:
|
||||
if not self.config.date_in_want_range(date):
|
||||
entry_data['_hook_cancel'] = True
|
||||
return False
|
||||
|
|
|
@ -57,8 +57,7 @@ class DateRangeConfig:
|
|||
def test_filter_by_date(entry_date, start_date, end_date, allowed):
|
||||
entry_data = {'date': entry_date}
|
||||
hook = filter_by_date.FilterByDateHook(DateRangeConfig(start_date, end_date))
|
||||
hook.run(entry_data)
|
||||
assert entry_data.get('_hook_cancel', False) == (not allowed)
|
||||
assert hook.run(entry_data) is (None if allowed else False)
|
||||
|
||||
class DefaultDateConfig:
|
||||
ONE_DAY = datetime.timedelta(days=1)
|
||||
|
|
Loading…
Reference in a new issue