1# -*- coding: utf-8 -*- 2 3"""Main `cookiecutter` CLI.""" 4 5import os 6import sys 7import json 8import collections 9 10import click 11 12from cookiecutter import __version__ 13from cookiecutter.log import configure_logger 14from cookiecutter.main import cookiecutter 15from cookiecutter.exceptions import ( 16 OutputDirExistsException, 17 InvalidModeException, 18 FailedHookException, 19 UndefinedVariableInTemplate, 20 UnknownExtension, 21 InvalidZipRepository, 22 RepositoryNotFound, 23 RepositoryCloneFailed, 24) 25 26 27def version_msg(): 28 """Return the Cookiecutter version, location and Python powering it.""" 29 python_version = sys.version[:3] 30 location = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 31 message = u'Cookiecutter %(version)s from {} (Python {})' 32 return message.format(location, python_version) 33 34 35def validate_extra_context(ctx, param, value): 36 """Validate extra context.""" 37 for s in value: 38 if '=' not in s: 39 raise click.BadParameter( 40 'EXTRA_CONTEXT should contain items of the form key=value; ' 41 "'{}' doesn't match that form".format(s) 42 ) 43 44 # Convert tuple -- e.g.: (u'program_name=foobar', u'startsecs=66') 45 # to dict -- e.g.: {'program_name': 'foobar', 'startsecs': '66'} 46 return collections.OrderedDict(s.split('=', 1) for s in value) or None 47 48 49@click.command(context_settings=dict(help_option_names=[u'-h', u'--help'])) 50@click.version_option(__version__, u'-V', u'--version', message=version_msg()) 51@click.argument(u'template') 52@click.argument(u'extra_context', nargs=-1, callback=validate_extra_context) 53@click.option( 54 u'--no-input', 55 is_flag=True, 56 help=u'Do not prompt for parameters and only use cookiecutter.json ' 57 u'file content', 58) 59@click.option( 60 u'-c', u'--checkout', help=u'branch, tag or commit to checkout after git clone', 61) 62@click.option( 63 u'--directory', 64 help=u'Directory within repo that holds cookiecutter.json file ' 65 u'for advanced repositories with multi templates in it', 66) 67@click.option( 68 '-v', '--verbose', is_flag=True, help='Print debug information', default=False 69) 70@click.option( 71 u'--replay', 72 is_flag=True, 73 help=u'Do not prompt for parameters and only use information entered ' 74 u'previously', 75) 76@click.option( 77 u'-f', 78 u'--overwrite-if-exists', 79 is_flag=True, 80 help=u'Overwrite the contents of the output directory if it already exists', 81) 82@click.option( 83 u'-s', 84 u'--skip-if-file-exists', 85 is_flag=True, 86 help=u'Skip the files in the corresponding directories if they already ' u'exist', 87 default=False, 88) 89@click.option( 90 u'-o', 91 u'--output-dir', 92 default='.', 93 type=click.Path(), 94 help=u'Where to output the generated project dir into', 95) 96@click.option( 97 u'--config-file', type=click.Path(), default=None, help=u'User configuration file' 98) 99@click.option( 100 u'--default-config', 101 is_flag=True, 102 help=u'Do not load a config file. Use the defaults instead', 103) 104@click.option( 105 u'--debug-file', 106 type=click.Path(), 107 default=None, 108 help=u'File to be used as a stream for DEBUG logging', 109) 110def main( 111 template, 112 extra_context, 113 no_input, 114 checkout, 115 verbose, 116 replay, 117 overwrite_if_exists, 118 output_dir, 119 config_file, 120 default_config, 121 debug_file, 122 directory, 123 skip_if_file_exists, 124): 125 """Create a project from a Cookiecutter project template (TEMPLATE). 126 127 Cookiecutter is free and open source software, developed and managed by 128 volunteers. If you would like to help out or fund the project, please get 129 in touch at https://github.com/audreyr/cookiecutter. 130 """ 131 # If you _need_ to support a local template in a directory 132 # called 'help', use a qualified path to the directory. 133 if template == u'help': 134 click.echo(click.get_current_context().get_help()) 135 sys.exit(0) 136 137 configure_logger(stream_level='DEBUG' if verbose else 'INFO', debug_file=debug_file) 138 139 try: 140 cookiecutter( 141 template, 142 checkout, 143 no_input, 144 extra_context=extra_context, 145 replay=replay, 146 overwrite_if_exists=overwrite_if_exists, 147 output_dir=output_dir, 148 config_file=config_file, 149 default_config=default_config, 150 password=os.environ.get('COOKIECUTTER_REPO_PASSWORD'), 151 directory=directory, 152 skip_if_file_exists=skip_if_file_exists, 153 ) 154 except ( 155 OutputDirExistsException, 156 InvalidModeException, 157 FailedHookException, 158 UnknownExtension, 159 InvalidZipRepository, 160 RepositoryNotFound, 161 RepositoryCloneFailed, 162 ) as e: 163 click.echo(e) 164 sys.exit(1) 165 except UndefinedVariableInTemplate as undefined_err: 166 click.echo('{}'.format(undefined_err.message)) 167 click.echo('Error message: {}'.format(undefined_err.error.message)) 168 169 context_str = json.dumps(undefined_err.context, indent=4, sort_keys=True) 170 click.echo('Context: {}'.format(context_str)) 171 sys.exit(1) 172 173 174if __name__ == "__main__": 175 main() 176