<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def fetch_modules(config, relative_path, download_directory):
 """ Assemble modules which will be included in CMakeLists.txt. """

    from collections import Iterable, namedtuple, defaultdict
    from autocmake.extract import extract_list, to_d, to_l
    from autocmake.parse_rst import parse_cmake_module

    cleaned_config = defaultdict(lambda: [])

    modules = []
    Module = namedtuple('Module', 'path name')

    num_sources = len(extract_list(config, 'source'))

    print_progress_bar(text='- assembling modules:',
                       done=0,
                       total=num_sources,
                       width=30)

    if 'modules' in config:
        i = 0
        for t in config['modules']:
            for k, v in t.items():

                d = to_d(v)
                for _k, _v in to_d(v).items():
                    cleaned_config[_k] = flat_add(cleaned_config[_k], _v)

                # fetch sources and parse them
                if 'source' in d:
                    for src in to_l(d['source']):
                        i += 1

                        # we download the file
                        module_name = os.path.basename(src)
                        if 'http' in src:
                            path = download_directory
                            name = 'autocmake_{0}'.format(module_name)
                            dst = os.path.join(download_directory, 'autocmake_{0}'.format(module_name))
                            fetch_url(src, dst)
                            file_name = dst
                            fetch_dst_directory = download_directory
                        else:
                            if os.path.exists(src):
                                path = os.path.dirname(src)
                                name = module_name
                                file_name = src
                                fetch_dst_directory = path
                            else:
                                sys.stderr.write("ERROR: {0} does not exist\n".format(src))
                                sys.exit(-1)

                        # we infer config from the module documentation
                        # dictionary d overrides the configuration in the module documentation
                        # this allows to override interpolation inside the module
                        with open(file_name, 'r') as f:
                            parsed_config = parse_cmake_module(f.read(), d)
                            for _k2, _v2 in parsed_config.items():
                                if _k2 not in to_d(v):
                                    # we add to clean_config only if the entry does not exist
                                    # in parent autocmake.yml already
                                    # this allows to override
                                    cleaned_config[_k2] = flat_add(cleaned_config[_k2], _v2)

                        modules.append(Module(path=path, name=name))
                        print_progress_bar(text='- assembling modules:',
                                           done=i,
                                           total=num_sources,
                                           width=30)
        print('')

    return modules, cleaned_config