Completed
Push — extra-context-cli ( af51e0...e7614b )
by Michael
01:02
created

cookiecutter.cookiecutter()   C

Complexity

Conditions 7

Size

Total Lines 74

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 7
dl 0
loc 74
rs 5.45

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
"""
5
cookiecutter.main
6
-----------------
7
8
Main entry point for the `cookiecutter` command.
9
10
The code in this module is also a good example of how to use Cookiecutter as a
11
library rather than a script.
12
"""
13
14
from __future__ import unicode_literals
15
import logging
16
import os
17
import re
18
19
from .config import get_user_config, USER_CONFIG_PATH
20
from .exceptions import InvalidModeException, RepositoryNotFound
21
from .prompt import prompt_for_config
22
from .generate import generate_context, generate_files
23
from .vcs import clone
24
from .replay import dump, load
25
26
logger = logging.getLogger(__name__)
27
28
builtin_abbreviations = {
29
    'gh': 'https://github.com/{0}.git',
30
    'bb': 'https://bitbucket.org/{0}',
31
}
32
33
REPO_REGEX = re.compile(r"""
34
(?x)
35
((((git|hg)\+)?(git|ssh|https?):(//)?)  # something like git:// ssh:// etc.
36
 |                                      # or
37
 (\w+@[\w\.]+)                          # something like user@...
38
)
39
""")
40
41
42
def is_repo_url(value):
43
    """Return True if value is a repository URL."""
44
    return bool(REPO_REGEX.match(value))
45
46
47
def expand_abbreviations(template, config_dict):
48
    """
49
    Expand abbreviations in a template name.
50
51
    :param template: The project template name.
52
    :param config_dict: The user config, which will contain abbreviation
53
        definitions.
54
    """
55
56
    abbreviations = builtin_abbreviations.copy()
57
    abbreviations.update(config_dict.get('abbreviations', {}))
58
59
    if template in abbreviations:
60
        return abbreviations[template]
61
62
    # Split on colon. If there is no colon, rest will be empty
63
    # and prefix will be the whole template
64
    prefix, sep, rest = template.partition(':')
65
    if prefix in abbreviations:
66
        return abbreviations[prefix].format(rest)
67
68
    return template
69
70
71
def cookiecutter(
72
        template, checkout=None, no_input=False, extra_context=None,
73
        replay=False, overwrite_if_exists=False, output_dir='.',
74
        config_file=USER_CONFIG_PATH):
75
    """
76
    API equivalent to using Cookiecutter at the command line.
77
78
    :param template: A directory containing a project template directory,
79
        or a URL to a git repository.
80
    :param checkout: The branch, tag or commit ID to checkout after clone.
81
    :param no_input: Prompt the user at command line for manual configuration?
82
    :param extra_context: A dictionary of context that overrides default
83
        and user configuration.
84
    :param: overwrite_if_exists: Overwrite the contents of output directory
85
        if it exists
86
    :param output_dir: Where to output the generated project dir into.
87
    :param config_file: User configuration file path.
88
    """
89
    if replay and ((no_input is not False) or (extra_context is not None)):
90
        err_msg = (
91
            "You can not use both replay and no_input or extra_context "
92
            "at the same time."
93
        )
94
        raise InvalidModeException(err_msg)
95
96
    # Get user config from ~/.cookiecutterrc or equivalent
97
    # If no config file, sensible defaults from config.DEFAULT_CONFIG are used
98
    config_dict = get_user_config(config_file=config_file)
99
100
    template = expand_abbreviations(template, config_dict)
101
102
    if is_repo_url(template):
103
        repo_dir = clone(
104
            repo_url=template,
105
            checkout=checkout,
106
            clone_to_dir=config_dict['cookiecutters_dir'],
107
            no_input=no_input
108
        )
109
    else:
110
        # If it's a local repo, no need to clone or copy to your
111
        # cookiecutters_dir
112
        repo_dir = template
113
114
    if not os.path.isdir(repo_dir):
115
        raise RepositoryNotFound(
116
            'The repository {0} could not be located.'.format(template)
117
        )
118
119
    template_name = os.path.basename(template)
120
121
    if replay:
122
        context = load(config_dict['replay_dir'], template_name)
123
    else:
124
        context_file = os.path.join(repo_dir, 'cookiecutter.json')
125
        logging.debug('context_file is {0}'.format(context_file))
126
127
        context = generate_context(
128
            context_file=context_file,
129
            default_context=config_dict['default_context'],
130
            extra_context=extra_context,
131
        )
132
133
        # prompt the user to manually configure at the command line.
134
        # except when 'no-input' flag is set
135
        context['cookiecutter'] = prompt_for_config(context, no_input)
136
137
        dump(config_dict['replay_dir'], template_name, context)
138
139
    # Create project from local context and project template.
140
    return generate_files(
141
        repo_dir=repo_dir,
142
        context=context,
143
        overwrite_if_exists=overwrite_if_exists,
144
        output_dir=output_dir
145
    )
146