Completed
Push — local-repository-not-found ( 7c0ac4 )
by Michael
01:00
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 = """
34
(
35
((git|ssh|https|http):(//)?)    # something like git:// ssh:// etc.
36
 |                              # or
37
 (\w+@[\w\.]+)                  # something like user@...
38
)
39
.*
40
"""
41
42
43
def is_repo_url(value):
44
    """Return True if value is a repository URL."""
45
    return bool(re.match(REPO_REGEX, value, re.VERBOSE))
46
47
48
def expand_abbreviations(template, config_dict):
49
    """
50
    Expand abbreviations in a template name.
51
52
    :param template: The project template name.
53
    :param config_dict: The user config, which will contain abbreviation
54
        definitions.
55
    """
56
57
    abbreviations = builtin_abbreviations.copy()
58
    abbreviations.update(config_dict.get('abbreviations', {}))
59
60
    if template in abbreviations:
61
        return abbreviations[template]
62
63
    # Split on colon. If there is no colon, rest will be empty
64
    # and prefix will be the whole template
65
    prefix, sep, rest = template.partition(':')
66
    if prefix in abbreviations:
67
        return abbreviations[prefix].format(rest)
68
69
    return template
70
71
72
def cookiecutter(
73
        template, checkout=None, no_input=False, extra_context=None,
74
        replay=False, overwrite_if_exists=False, output_dir='.',
75
        config_file=USER_CONFIG_PATH):
76
    """
77
    API equivalent to using Cookiecutter at the command line.
78
79
    :param template: A directory containing a project template directory,
80
        or a URL to a git repository.
81
    :param checkout: The branch, tag or commit ID to checkout after clone.
82
    :param no_input: Prompt the user at command line for manual configuration?
83
    :param extra_context: A dictionary of context that overrides default
84
        and user configuration.
85
    :param: overwrite_if_exists: Overwrite the contents of output directory
86
        if it exists
87
    :param output_dir: Where to output the generated project dir into.
88
    :param config_file: User configuration file path.
89
    """
90
    if replay and ((no_input is not False) or (extra_context is not None)):
91
        err_msg = (
92
            "You can not use both replay and no_input or extra_context "
93
            "at the same time."
94
        )
95
        raise InvalidModeException(err_msg)
96
97
    # Get user config from ~/.cookiecutterrc or equivalent
98
    # If no config file, sensible defaults from config.DEFAULT_CONFIG are used
99
    config_dict = get_user_config(config_file=config_file)
100
101
    template = expand_abbreviations(template, config_dict)
102
103
    if is_repo_url(template):
104
        repo_dir = clone(
105
            repo_url=template,
106
            checkout=checkout,
107
            clone_to_dir=config_dict['cookiecutters_dir'],
108
            no_input=no_input
109
        )
110
    else:
111
        # If it's a local repo, no need to clone or copy to your
112
        # cookiecutters_dir
113
        repo_dir = template
114
115
    if not os.path.isdir(repo_dir):
116
        raise RepositoryNotFound(
117
            'The repository {0} could not be located'.format(template)
118
        )
119
120
    template_name = os.path.basename(template)
121
122
    if replay:
123
        context = load(config_dict['replay_dir'], template_name)
124
    else:
125
        context_file = os.path.join(repo_dir, 'cookiecutter.json')
126
        logging.debug('context_file is {0}'.format(context_file))
127
128
        context = generate_context(
129
            context_file=context_file,
130
            default_context=config_dict['default_context'],
131
            extra_context=extra_context,
132
        )
133
134
        # prompt the user to manually configure at the command line.
135
        # except when 'no-input' flag is set
136
        context['cookiecutter'] = prompt_for_config(context, no_input)
137
138
        dump(config_dict['replay_dir'], template_name, context)
139
140
    # Create project from local context and project template.
141
    return generate_files(
142
        repo_dir=repo_dir,
143
        context=context,
144
        overwrite_if_exists=overwrite_if_exists,
145
        output_dir=output_dir
146
    )
147