unzip()   F
last analyzed

Complexity

Conditions 17

Size

Total Lines 107

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 17
c 2
b 0
f 0
dl 0
loc 107
rs 2

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