cookiecutter.zipfile.unzip()   F
last analyzed

Complexity

Conditions 17

Size

Total Lines 107
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 60
dl 0
loc 107
rs 1.8
c 0
b 0
f 0
cc 17
nop 5

How to fix   Long Method    Complexity   

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:

Complexity

Complex classes like cookiecutter.zipfile.unzip() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""Utility functions for handling and fetching repo archives in zip format."""
2
3
from __future__ import absolute_import
4
5
import os
6
import tempfile
7
from zipfile import ZipFile
8
9
import requests
10
11
try:
12
    # BadZipfile was renamed to BadZipFile in Python 3.2.
13
    from zipfile import BadZipFile
14
except ImportError:
15
    from zipfile import BadZipfile as BadZipFile
16
17
from cookiecutter.exceptions import InvalidZipRepository
18
from cookiecutter.prompt import read_repo_password
19
from cookiecutter.utils import make_sure_path_exists, prompt_and_delete
20
21
22
def unzip(zip_uri, is_url, clone_to_dir='.', no_input=False, password=None):
23
    """Download and unpack a zipfile at a given URI.
24
25
    This will download the zipfile to the cookiecutter repository,
26
    and unpack into a temporary directory.
27
28
    :param zip_uri: The URI for the zipfile.
29
    :param is_url: Is the zip URI a URL or a file?
30
    :param clone_to_dir: The cookiecutter repository directory
31
        to put the archive into.
32
    :param no_input: Suppress any prompts
33
    :param password: The password to use when unpacking the repository.
34
    """
35
    # Ensure that clone_to_dir exists
36
    clone_to_dir = os.path.expanduser(clone_to_dir)
37
    make_sure_path_exists(clone_to_dir)
38
39
    if is_url:
40
        # Build the name of the cached zipfile,
41
        # and prompt to delete if it already exists.
42
        identifier = zip_uri.rsplit('/', 1)[1]
43
        zip_path = os.path.join(clone_to_dir, identifier)
44
45
        if os.path.exists(zip_path):
46
            download = prompt_and_delete(zip_path, no_input=no_input)
47
        else:
48
            download = True
49
50
        if download:
51
            # (Re) download the zipfile
52
            r = requests.get(zip_uri, stream=True)
53
            with open(zip_path, 'wb') as f:
54
                for chunk in r.iter_content(chunk_size=1024):
55
                    if chunk:  # filter out keep-alive new chunks
56
                        f.write(chunk)
57
    else:
58
        # Just use the local zipfile as-is.
59
        zip_path = os.path.abspath(zip_uri)
60
61
    # Now unpack the repository. The zipfile will be unpacked
62
    # into a temporary directory
63
    try:
64
        zip_file = ZipFile(zip_path)
65
66
        if len(zip_file.namelist()) == 0:
67
            raise InvalidZipRepository(
68
                'Zip repository {} is empty'.format(zip_uri)
69
            )
70
71
        # The first record in the zipfile should be the directory entry for
72
        # the archive. If it isn't a directory, there's a problem.
73
        first_filename = zip_file.namelist()[0]
74
        if not first_filename.endswith('/'):
75
            raise InvalidZipRepository(
76
                'Zip repository {} does not include '
77
                'a top-level directory'.format(zip_uri)
78
            )
79
80
        # Construct the final target directory
81
        project_name = first_filename[:-1]
82
        unzip_base = tempfile.mkdtemp()
83
        unzip_path = os.path.join(unzip_base, project_name)
84
85
        # Extract the zip file into the temporary directory
86
        try:
87
            zip_file.extractall(path=unzip_base)
88
        except RuntimeError:
89
            # File is password protected; try to get a password from the
90
            # environment; if that doesn't work, ask the user.
91
            if password is not None:
92
                try:
93
                    zip_file.extractall(
94
                        path=unzip_base,
95
                        pwd=password.encode('utf-8')
96
                    )
97
                except RuntimeError:
98
                    raise InvalidZipRepository(
99
                        'Invalid password provided for protected repository'
100
                    )
101
            elif no_input:
102
                raise InvalidZipRepository(
103
                    'Unable to unlock password protected repository'
104
                )
105
            else:
106
                retry = 0
107
                while retry is not None:
108
                    try:
109
                        password = read_repo_password('Repo password')
110
                        zip_file.extractall(
111
                            path=unzip_base,
112
                            pwd=password.encode('utf-8')
113
                        )
114
                        retry = None
115
                    except RuntimeError:
116
                        retry += 1
117
                        if retry == 3:
118
                            raise InvalidZipRepository(
119
                                'Invalid password provided '
120
                                'for protected repository'
121
                            )
122
123
    except BadZipFile:
124
        raise InvalidZipRepository(
125
            'Zip repository {} is not a valid zip archive:'.format(zip_uri)
126
        )
127
128
    return unzip_path
129