<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def main(output_path=None):
 """Writes out new python build system This is needed because CircleCI does not support build matrices nor parameterisation of cache paths or other aspects of their config There's also the added bonus of validating the yaml as we go. Additionally, we template and write Docker and docker-compose files for multiple python versions, as Docker `FROM` statements are also un-templatable using environment variables or similar. The bulk of the config structure is parsed templated yaml, which seems the most succinct way of building deeply nested dictionaries and lists, and also cleanly maps to the appearance of config.yml before & after templating. The main job blocks (build, test, deploy) are expanded as the product of python versions and mbed cloud environments, before being recombined into the job listing. Jobs are chained into a CircleCI workflow using a graph (in which nodes are job identifiers, and edges describe the dependencies and any additional parameters) """

    config_output_file = output_path or os.path.join(PROJECT_ROOT, '.circleci', 'config.yml')
    yaml_structure = generate_circle_output()
    with open(config_output_file, 'w') as fh:
        yaml_content = yaml.safe_dump(data=yaml_structure, default_flow_style=False)
        fh.write(
            f'#\n'
            f'# This file is autogenerated, do not modify manually. '
            f'See {author_file} for instructions.\n'
            f'#\n'
            f'{yaml_content}'
        )

    for path, content in generate_docker_targets().items():
        LOG.info('writing %s', path)
        with open(path, 'w') as fh:
            fh.write(content)