Search code examples
pythonpython-3.xdoit

Python doit: Create subtasks using file generated from previous task


With Python doit I want to generate sub-tasks by reading a list of files from a file (that was generated in an earlier task), then yielding actions for each of those files:

def task_generates_list_of_files():

    def generate_file():

        with open('list_of_files.txt', 'w') as fo:

            for x in range(20):
                fo.write(f'thing_{x}.txt\n')

    return {
        'actions': [generate_file],
        'targets': ['list_of_files.txt']
    }

def task_generate_subtasks_using_file():

    with open('list_of_files.txt', 'r') as fo:

        for filename in fo:

            filename = filename.strip()

            yield {
                'name': filename,
                'actions': [f'program_to_run {filename}'],
                'file_dep': ['list_of_files.txt']
            }

However, because list_of_files.txt doesn't exist when the tasks are being set up by doit, it raises a FileNotFoundError exception.

I have seen this answer but am not clear that getargs can work when generating subtasks, because I wouldn't be able to loop through the list of files until they were injected into a Python action, at which point I can't yield any sub-tasks. Doing that results in:

Python Task error:...
It must return:
False for failed task.
True, None, string or dict for successful task
returned <generator object...

Solution

  • In the end, I'd missed something I hadn't seen before in the doit documentation: delayed tasks. You can use create_after to defer creation of a task until after a given task is executed.

    In my case this allows list_of_files.txt to be generated by task_generates_list_of_files before list_of_files.txt is read within the task definition of task_generate_subtasks_using_file:

    from doit import create_after
    
    # Show output
    DOIT_CONFIG = {
        'verbosity': 2
    }
    
    def task_generates_list_of_files():
    
        def generate_file():
    
            with open('list_of_files.txt', 'w') as fo:
    
                for x in range(20):
                    fo.write(f'thing_{x}.txt\n')
    
        return {
            'actions': [generate_file],
            'targets': ['list_of_files.txt']
        }
    
    @create_after('generates_list_of_files')
    def task_generate_subtasks_using_file():
    
        with open('list_of_files.txt', 'r') as fo:
    
            for filename in fo:
    
                filename = filename.strip()
    
                yield {
                    'name': filename,
                    'actions': [f'echo {filename}'],
                    'file_dep': ['list_of_files.txt']
                }