##// END OF EJS Templates
Use os.remove instead of shutil.rmtree if we try to remove a symbolic link...
Jason Grout -
Show More
@@ -1,265 +1,265
1 1 # coding: utf-8
2 2 """Utilities for installing Javascript extensions for the notebook"""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from __future__ import print_function
8 8
9 9 import os
10 10 import shutil
11 11 import tarfile
12 12 import zipfile
13 13 from os.path import basename, join as pjoin
14 14
15 15 # Deferred imports
16 16 try:
17 17 from urllib.parse import urlparse # Py3
18 18 from urllib.request import urlretrieve
19 19 except ImportError:
20 20 from urlparse import urlparse
21 21 from urllib import urlretrieve
22 22
23 23 from IPython.utils.path import get_ipython_dir, ensure_dir_exists
24 24 from IPython.utils.py3compat import string_types, cast_unicode_py2
25 25 from IPython.utils.tempdir import TemporaryDirectory
26 26
27 27
28 28 def _should_copy(src, dest, verbose=1):
29 29 """should a file be copied?"""
30 30 if not os.path.exists(dest):
31 31 return True
32 32 if os.stat(dest).st_mtime < os.stat(src).st_mtime:
33 33 if verbose >= 2:
34 34 print("%s is out of date" % dest)
35 35 return True
36 36 if verbose >= 2:
37 37 print("%s is up to date" % dest)
38 38 return False
39 39
40 40
41 41 def _maybe_copy(src, dest, verbose=1):
42 42 """copy a file if it needs updating"""
43 43 if _should_copy(src, dest, verbose):
44 44 if verbose >= 1:
45 45 print("copying %s -> %s" % (src, dest))
46 46 shutil.copy2(src, dest)
47 47
48 48
49 49 def _safe_is_tarfile(path):
50 50 """safe version of is_tarfile, return False on IOError"""
51 51 try:
52 52 return tarfile.is_tarfile(path)
53 53 except IOError:
54 54 return False
55 55
56 56
57 57 def check_nbextension(files, ipython_dir=None):
58 58 """Check whether nbextension files have been installed
59 59
60 60 files should be a list of relative paths within nbextensions.
61 61
62 62 Returns True if all files are found, False if any are missing.
63 63 """
64 64 ipython_dir = ipython_dir or get_ipython_dir()
65 65 nbext = pjoin(ipython_dir, u'nbextensions')
66 66 # make sure nbextensions dir exists
67 67 if not os.path.exists(nbext):
68 68 return False
69 69
70 70 if isinstance(files, string_types):
71 71 # one file given, turn it into a list
72 72 files = [files]
73 73
74 74 return all(os.path.exists(pjoin(nbext, f)) for f in files)
75 75
76 76
77 77 def install_nbextension(files, overwrite=False, symlink=False, ipython_dir=None, verbose=1):
78 78 """Install a Javascript extension for the notebook
79 79
80 80 Stages files and/or directories into IPYTHONDIR/nbextensions.
81 81 By default, this compares modification time, and only stages files that need updating.
82 82 If `overwrite` is specified, matching files are purged before proceeding.
83 83
84 84 Parameters
85 85 ----------
86 86
87 87 files : list(paths or URLs)
88 88 One or more paths or URLs to existing files directories to install.
89 89 These will be installed with their base name, so '/path/to/foo'
90 90 will install to 'nbextensions/foo'.
91 91 Archives (zip or tarballs) will be extracted into the nbextensions directory.
92 92 overwrite : bool [default: False]
93 93 If True, always install the files, regardless of what may already be installed.
94 94 symlink : bool [default: False]
95 95 If True, create a symlink in nbextensions, rather than copying files.
96 96 Not allowed with URLs or archives. Windows support for symlinks requires
97 97 Vista or above, Python 3, and a permission bit which only admin users
98 98 have by default, so don't rely on it.
99 99 ipython_dir : str [optional]
100 100 The path to an IPython directory, if the default value is not desired.
101 101 get_ipython_dir() is used by default.
102 102 verbose : int [default: 1]
103 103 Set verbosity level. The default is 1, where file actions are printed.
104 104 set verbose=2 for more output, or verbose=0 for silence.
105 105 """
106 106
107 107 ipython_dir = ipython_dir or get_ipython_dir()
108 108 nbext = pjoin(ipython_dir, u'nbextensions')
109 109 # make sure nbextensions dir exists
110 110 ensure_dir_exists(nbext)
111 111
112 112 if isinstance(files, string_types):
113 113 # one file given, turn it into a list
114 114 files = [files]
115 115
116 116 for path in map(cast_unicode_py2, files):
117 117
118 118 if path.startswith(('https://', 'http://')):
119 119 if symlink:
120 120 raise ValueError("Cannot symlink from URLs")
121 121 # Given a URL, download it
122 122 with TemporaryDirectory() as td:
123 123 filename = urlparse(path).path.split('/')[-1]
124 124 local_path = os.path.join(td, filename)
125 125 if verbose >= 1:
126 126 print("downloading %s to %s" % (path, local_path))
127 127 urlretrieve(path, local_path)
128 128 # now install from the local copy
129 129 install_nbextension(local_path, overwrite, symlink, ipython_dir, verbose)
130 130 continue
131 131
132 132 # handle archives
133 133 archive = None
134 134 if path.endswith('.zip'):
135 135 archive = zipfile.ZipFile(path)
136 136 elif _safe_is_tarfile(path):
137 137 archive = tarfile.open(path)
138 138
139 139 if archive:
140 140 if symlink:
141 141 raise ValueError("Cannot symlink from archives")
142 142 if verbose >= 1:
143 143 print("extracting %s to %s" % (path, nbext))
144 144 archive.extractall(nbext)
145 145 archive.close()
146 146 continue
147 147
148 148 dest = pjoin(nbext, basename(path))
149 149 if overwrite and os.path.exists(dest):
150 150 if verbose >= 1:
151 151 print("removing %s" % dest)
152 if os.path.isdir(dest):
152 if os.path.isdir(dest) and not os.path.islink(dest):
153 153 shutil.rmtree(dest)
154 154 else:
155 155 os.remove(dest)
156 156
157 157 if symlink:
158 158 path = os.path.abspath(path)
159 159 if not os.path.exists(dest):
160 160 if verbose >= 1:
161 161 print("symlink %s -> %s" % (dest, path))
162 162 os.symlink(path, dest)
163 163 continue
164 164
165 165 if os.path.isdir(path):
166 166 strip_prefix_len = len(path) - len(basename(path))
167 167 for parent, dirs, files in os.walk(path):
168 168 dest_dir = pjoin(nbext, parent[strip_prefix_len:])
169 169 if not os.path.exists(dest_dir):
170 170 if verbose >= 2:
171 171 print("making directory %s" % dest_dir)
172 172 os.makedirs(dest_dir)
173 173 for file in files:
174 174 src = pjoin(parent, file)
175 175 # print("%r, %r" % (dest_dir, file))
176 176 dest = pjoin(dest_dir, file)
177 177 _maybe_copy(src, dest, verbose)
178 178 else:
179 179 src = path
180 180 _maybe_copy(src, dest, verbose)
181 181
182 182 #----------------------------------------------------------------------
183 183 # install nbextension app
184 184 #----------------------------------------------------------------------
185 185
186 186 from IPython.utils.traitlets import Bool, Enum
187 187 from IPython.core.application import BaseIPythonApplication
188 188
189 189 flags = {
190 190 "overwrite" : ({
191 191 "NBExtensionApp" : {
192 192 "overwrite" : True,
193 193 }}, "Force overwrite of existing files"
194 194 ),
195 195 "debug" : ({
196 196 "NBExtensionApp" : {
197 197 "verbose" : 2,
198 198 }}, "Extra output"
199 199 ),
200 200 "quiet" : ({
201 201 "NBExtensionApp" : {
202 202 "verbose" : 0,
203 203 }}, "Minimal output"
204 204 ),
205 205 "symlink" : ({
206 206 "NBExtensionApp" : {
207 207 "symlink" : True,
208 208 }}, "Create symlinks instead of copying files"
209 209 ),
210 210 }
211 211 flags['s'] = flags['symlink']
212 212
213 213 aliases = {
214 214 "ipython-dir" : "NBExtensionApp.ipython_dir"
215 215 }
216 216
217 217 class NBExtensionApp(BaseIPythonApplication):
218 218 """Entry point for installing notebook extensions"""
219 219
220 220 description = """Install IPython notebook extensions
221 221
222 222 Usage
223 223
224 224 ipython install-nbextension file [more files, folders, archives or urls]
225 225
226 226 This copies files and/or folders into the IPython nbextensions directory.
227 227 If a URL is given, it will be downloaded.
228 228 If an archive is given, it will be extracted into nbextensions.
229 229 If the requested files are already up to date, no action is taken
230 230 unless --overwrite is specified.
231 231 """
232 232
233 233 examples = """
234 234 ipython install-nbextension /path/to/d3.js /path/to/myextension
235 235 """
236 236 aliases = aliases
237 237 flags = flags
238 238
239 239 overwrite = Bool(False, config=True, help="Force overwrite of existing files")
240 240 symlink = Bool(False, config=True, help="Create symlinks instead of copying files")
241 241 verbose = Enum((0,1,2), default_value=1, config=True,
242 242 help="Verbosity level"
243 243 )
244 244
245 245 def install_extensions(self):
246 246 install_nbextension(self.extra_args,
247 247 overwrite=self.overwrite,
248 248 symlink=self.symlink,
249 249 verbose=self.verbose,
250 250 ipython_dir=self.ipython_dir,
251 251 )
252 252
253 253 def start(self):
254 254 if not self.extra_args:
255 255 nbext = pjoin(self.ipython_dir, u'nbextensions')
256 256 print("Notebook extensions in %s:" % nbext)
257 257 for ext in os.listdir(nbext):
258 258 print(u" %s" % ext)
259 259 else:
260 260 self.install_extensions()
261 261
262 262
263 263 if __name__ == '__main__':
264 264 NBExtensionApp.launch_instance()
265 265 No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now