Passed
Push — master ( 0b4062...c0e769 )
by
unknown
05:31
created

cookiecutter.utils.simple_filter()   A

Complexity

Conditions 1

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
"""Helper functions used throughout Cookiecutter."""
2
import contextlib
3
import errno
4
import logging
5
import os
6
import shutil
7
import stat
8
import sys
9
10
from cookiecutter.prompt import read_user_yes_no
11
from jinja2.ext import Extension
12
13
logger = logging.getLogger(__name__)
14
15
16
def force_delete(func, path, exc_info):
17
    """Error handler for `shutil.rmtree()` equivalent to `rm -rf`.
18
19
    Usage: `shutil.rmtree(path, onerror=force_delete)`
20
    From https://docs.python.org/3/library/shutil.html#rmtree-example
21
    """
22
    os.chmod(path, stat.S_IWRITE)
23
    func(path)
24
25
26
def rmtree(path):
27
    """Remove a directory and all its contents. Like rm -rf on Unix.
28
29
    :param path: A directory path.
30
    """
31
    shutil.rmtree(path, onerror=force_delete)
32
33
34
def make_sure_path_exists(path):
35
    """Ensure that a directory exists.
36
37
    :param path: A directory path.
38
    """
39
    logger.debug('Making sure path exists: %s', path)
40
    try:
41
        os.makedirs(path)
42
        logger.debug('Created directory at: %s', path)
43
    except OSError as exception:
44
        if exception.errno != errno.EEXIST:
45
            return False
46
    return True
47
48
49
@contextlib.contextmanager
50
def work_in(dirname=None):
51
    """Context manager version of os.chdir.
52
53
    When exited, returns to the working directory prior to entering.
54
    """
55
    curdir = os.getcwd()
56
    try:
57
        if dirname is not None:
58
            os.chdir(dirname)
59
        yield
60
    finally:
61
        os.chdir(curdir)
62
63
64
def make_executable(script_path):
65
    """Make `script_path` executable.
66
67
    :param script_path: The file to change
68
    """
69
    status = os.stat(script_path)
70
    os.chmod(script_path, status.st_mode | stat.S_IEXEC)
71
72
73
def prompt_and_delete(path, no_input=False):
74
    """
75
    Ask user if it's okay to delete the previously-downloaded file/directory.
76
77
    If yes, delete it. If no, checks to see if the old version should be
78
    reused. If yes, it's reused; otherwise, Cookiecutter exits.
79
80
    :param path: Previously downloaded zipfile.
81
    :param no_input: Suppress prompt to delete repo and just delete it.
82
    :return: True if the content was deleted
83
    """
84
    # Suppress prompt if called via API
85
    if no_input:
86
        ok_to_delete = True
87
    else:
88
        question = (
89
            "You've downloaded {} before. Is it okay to delete and re-download it?"
90
        ).format(path)
91
92
        ok_to_delete = read_user_yes_no(question, 'yes')
93
94
    if ok_to_delete:
95
        if os.path.isdir(path):
96
            rmtree(path)
97
        else:
98
            os.remove(path)
99
        return True
100
    else:
101
        ok_to_reuse = read_user_yes_no(
102
            "Do you want to re-use the existing version?", 'yes'
103
        )
104
105
        if ok_to_reuse:
106
            return False
107
108
        sys.exit()
109
110
111
def simple_filter(filter_function):
112
    """Decorate a function to wrap it in a simplified jinja2 extension."""
113
114
    class SimpleFilterExtension(Extension):
115
        def __init__(self, environment):
116
            super(SimpleFilterExtension, self).__init__(environment)
117
            environment.filters[filter_function.__name__] = filter_function
118
119
    SimpleFilterExtension.__name__ = filter_function.__name__
120
    return SimpleFilterExtension
121