|
1
|
|
|
from __future__ import absolute_import |
|
2
|
|
|
|
|
3
|
|
|
import os |
|
4
|
|
|
import requests |
|
5
|
|
|
import sys |
|
6
|
|
|
from zipfile import ZipFile |
|
7
|
|
|
|
|
8
|
|
|
from .prompt import read_user_yes_no |
|
9
|
|
|
from .utils import make_sure_path_exists, rmtree |
|
10
|
|
|
|
|
11
|
|
|
|
|
12
|
|
|
def prompt_and_delete(path, no_input=False): |
|
13
|
|
|
"""Ask the user whether it's okay to delete the previously-downloaded |
|
14
|
|
|
file/directory. |
|
15
|
|
|
|
|
16
|
|
|
If yes, deletes it. Otherwise, Cookiecutter exits. |
|
17
|
|
|
|
|
18
|
|
|
:param path: Previously downloaded zipfile. |
|
19
|
|
|
:param no_input: Suppress prompt to delete repo and just delete it. |
|
20
|
|
|
""" |
|
21
|
|
|
# Suppress prompt if called via API |
|
22
|
|
|
if no_input: |
|
23
|
|
|
ok_to_delete = True |
|
24
|
|
|
else: |
|
25
|
|
|
question = ( |
|
26
|
|
|
"You've downloaded {} before. " |
|
27
|
|
|
"Is it okay to delete and re-download it?" |
|
28
|
|
|
).format(path) |
|
29
|
|
|
|
|
30
|
|
|
ok_to_delete = read_user_yes_no(question, 'yes') |
|
31
|
|
|
|
|
32
|
|
|
if ok_to_delete: |
|
33
|
|
|
if os.path.isdir(path): |
|
34
|
|
|
rmtree(path) |
|
35
|
|
|
else: |
|
36
|
|
|
os.remove(path) |
|
37
|
|
|
else: |
|
38
|
|
|
sys.exit() |
|
39
|
|
|
|
|
40
|
|
|
return ok_to_delete |
|
41
|
|
|
|
|
42
|
|
|
|
|
43
|
|
|
def unzip(zip_url, is_url, clone_to_dir='.', no_input=False): |
|
44
|
|
|
# Ensure that clone_to_dir exists |
|
45
|
|
|
clone_to_dir = os.path.expanduser(clone_to_dir) |
|
46
|
|
|
make_sure_path_exists(clone_to_dir) |
|
47
|
|
|
|
|
48
|
|
|
if is_url: |
|
49
|
|
|
# Build the name of the cached zipfile, |
|
50
|
|
|
# and prompt to delete if it already exists. |
|
51
|
|
|
identifier = zip_url.rsplit('/', 1)[1] |
|
52
|
|
|
zip_path = os.path.join(clone_to_dir, identifier) |
|
53
|
|
|
|
|
54
|
|
|
if os.path.exists(zip_path): |
|
55
|
|
|
ok_to_delete = prompt_and_delete(zip_path, no_input=no_input) |
|
56
|
|
|
else: |
|
57
|
|
|
ok_to_delete = None |
|
58
|
|
|
|
|
59
|
|
|
# (Re) download the zipfile |
|
60
|
|
|
r = requests.get(zip_url, stream=True) |
|
61
|
|
|
with open(zip_path, 'wb') as f: |
|
62
|
|
|
for chunk in r.iter_content(chunk_size=1024): |
|
63
|
|
|
if chunk: # filter out keep-alive new chunks |
|
64
|
|
|
f.write(chunk) |
|
65
|
|
|
else: |
|
66
|
|
|
# Just use the local zipfile as-is. |
|
67
|
|
|
zip_path = os.path.abspath(zip_url) |
|
68
|
|
|
ok_to_delete = None |
|
69
|
|
|
|
|
70
|
|
|
# Now unpack the repository. The zipfile will include |
|
71
|
|
|
# the name of the template as the top level directory; |
|
72
|
|
|
# Check if that directory already exists, and if so, |
|
73
|
|
|
# prompt for deletion. If we've previously OK'd deletion, |
|
74
|
|
|
# don't ask again. |
|
75
|
|
|
zip_file = ZipFile(zip_path) |
|
76
|
|
|
unzip_name = zip_file.namelist()[0][:-1] |
|
77
|
|
|
|
|
78
|
|
|
unzip_path = os.path.join(clone_to_dir, unzip_name) |
|
79
|
|
|
if os.path.exists(unzip_path): |
|
80
|
|
|
if ok_to_delete is None: |
|
81
|
|
|
ok_to_delete = prompt_and_delete(unzip_path, no_input=no_input) |
|
82
|
|
|
else: |
|
83
|
|
|
rmtree(unzip_path) |
|
84
|
|
|
|
|
85
|
|
|
# Extract the zip file |
|
86
|
|
|
zip_file.extractall(path=clone_to_dir) |
|
87
|
|
|
|
|
88
|
|
|
return unzip_path |
|
89
|
|
|
|