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…
	
	Add table
		
		Reference in a new issue