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.
|
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,9 +92,14 @@ 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 entry_data:
|
if hook_return is False:
|
||||||
template.render(entry_data)
|
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.
|
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()
|
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:
|
||||||
|
entry_data = hook_retval
|
||||||
else:
|
else:
|
||||||
del entry_data['_hook_cancel']
|
|
||||||
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='')
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Reference in a new issue