Completed
Pull Request — master (#961)
by
unknown
32s
created

unzip()   F

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