##// END OF EJS Templates
Made profile the default install option, install_dir the explicit
Kyle Kelley -
Show More
@@ -1,328 +1,332 b''
1 #!/usr/bin/python
1 #!/usr/bin/python
2 """Utility function for installing MathJax javascript library into
2 """Utility function for installing MathJax javascript library into
3 the notebook's 'static' directory, for offline use.
3 the notebook's 'static' directory, for offline use.
4
4
5 Authors:
5 Authors:
6
6
7 * Min RK
7 * Min RK
8 * Mark Sienkiewicz
8 * Mark Sienkiewicz
9 * Matthias Bussonnier
9 * Matthias Bussonnier
10
10
11 To download and install MathJax:
11 To download and install MathJax:
12
12
13 From Python:
13 From Python:
14
14
15 >>> from IPython.external.mathjax import install_mathjax
15 >>> from IPython.external.mathjax import install_mathjax
16 >>> install_mathjax()
16 >>> install_mathjax()
17
17
18 From the command line:
18 From the command line:
19
19
20 $ python -m IPython.external.mathjax
20 $ python -m IPython.external.mathjax
21
21
22 To a specific profile:
23
24 $ python -m IPython.external.mathjax --profile=research
25
22 To install MathJax from a file you have already downloaded:
26 To install MathJax from a file you have already downloaded:
23
27
24 $ python -m IPython.external.mathjax mathjax-xxx.tar.gz
28 $ python -m IPython.external.mathjax mathjax-xxx.tar.gz
25 $ python -m IPython.external.mathjax mathjax-xxx.zip
29 $ python -m IPython.external.mathjax mathjax-xxx.zip
26
30
27 It will not install MathJax if it is already there. Use -r to
31 It will not install MathJax if it is already there. Use -r to
28 replace the existing copy of MathJax.
32 replace the existing copy of MathJax.
29
33
30 To find the directory where IPython would like MathJax installed:
34 To find the directory where IPython would like MathJax installed:
31
35
32 $ python -m IPython.external.mathjax -d
36 $ python -m IPython.external.mathjax -d
33
37
34 """
38 """
35
39
36
40
37 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
38 # Copyright (C) 2008-2011 The IPython Development Team
42 # Copyright (C) 2008-2011 The IPython Development Team
39 #
43 #
40 # Distributed under the terms of the BSD License. The full license is in
44 # Distributed under the terms of the BSD License. The full license is in
41 # the file COPYING, distributed as part of this software.
45 # the file COPYING, distributed as part of this software.
42 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
43
47
44
48
45 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
46 # Imports
50 # Imports
47 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
48
52
49 import os
53 import os
50 import shutil
54 import shutil
51 import sys
55 import sys
52 import tarfile
56 import tarfile
53 import urllib2
57 import urllib2
54 import zipfile
58 import zipfile
55
59
56
60
57 from IPython.utils.path import locate_profile
61 from IPython.utils.path import locate_profile
58 from IPython.external import argparse
62 from IPython.external import argparse
59 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
60 #
64 #
61 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
62
66
63 # Where mathjax will be installed.
67 # Where mathjax will be installed.
64
68
65 static = os.path.join(locate_profile('default'), 'static')
69 static = os.path.join(locate_profile('default'), 'static')
66 default_dest = os.path.join(static, 'mathjax')
70 default_dest = os.path.join(static, 'mathjax')
67
71
68 ##
72 ##
69
73
70 # Test for access to install mathjax.
74 # Test for access to install mathjax.
71
75
72 def check_perms(dest, replace=False):
76 def check_perms(dest, replace=False):
73 parent = os.path.abspath(os.path.join(dest, os.path.pardir))
77 parent = os.path.abspath(os.path.join(dest, os.path.pardir))
74 components = dest.split(os.path.sep)
78 components = dest.split(os.path.sep)
75 subpaths = [ os.path.sep+os.path.sep.join(components[1:i]) for i in range(1,len(components))]
79 subpaths = [ os.path.sep+os.path.sep.join(components[1:i]) for i in range(1,len(components))]
76
80
77 existing_path = filter(os.path.exists, subpaths)
81 existing_path = filter(os.path.exists, subpaths)
78 last_writable = existing_path[-1]
82 last_writable = existing_path[-1]
79 if not os.access(last_writable, os.W_OK):
83 if not os.access(last_writable, os.W_OK):
80 raise IOError("Need have write access to %s" % parent)
84 raise IOError("Need have write access to %s" % parent)
81 not_existing = [ path for path in subpaths if path not in existing_path]
85 not_existing = [ path for path in subpaths if path not in existing_path]
82 # subfolder we will create, will obviously be writable
86 # subfolder we will create, will obviously be writable
83 # should we still considere checking separately that
87 # should we still considere checking separately that
84 # ipython profiles have been created ?
88 # ipython profiles have been created ?
85 for folder in not_existing:
89 for folder in not_existing:
86 os.mkdir(folder)
90 os.mkdir(folder)
87
91
88 if os.path.exists(dest):
92 if os.path.exists(dest):
89 if replace:
93 if replace:
90 if not os.access(dest, os.W_OK):
94 if not os.access(dest, os.W_OK):
91 raise IOError("Need have write access to %s" % dest)
95 raise IOError("Need have write access to %s" % dest)
92 print "removing previous MathJax install"
96 print "removing previous MathJax install"
93 shutil.rmtree(dest)
97 shutil.rmtree(dest)
94 return True
98 return True
95 else:
99 else:
96 print "offline MathJax apparently already installed"
100 print "offline MathJax apparently already installed"
97 return False
101 return False
98 else :
102 else :
99 return True
103 return True
100
104
101 ##
105 ##
102
106
103 def extract_tar( fd, dest ) :
107 def extract_tar( fd, dest ) :
104 # use 'r|gz' stream mode, because socket file-like objects can't seek:
108 # use 'r|gz' stream mode, because socket file-like objects can't seek:
105 tar = tarfile.open(fileobj=fd, mode='r|gz')
109 tar = tarfile.open(fileobj=fd, mode='r|gz')
106
110
107 # we just happen to know that the first entry in the mathjax
111 # we just happen to know that the first entry in the mathjax
108 # archive is the directory that the remaining members are in.
112 # archive is the directory that the remaining members are in.
109 topdir = tar.firstmember.path
113 topdir = tar.firstmember.path
110
114
111 # extract the archive (contains a single directory) to the static/ directory
115 # extract the archive (contains a single directory) to the static/ directory
112 parent = os.path.abspath(os.path.join(dest, os.path.pardir))
116 parent = os.path.abspath(os.path.join(dest, os.path.pardir))
113 tar.extractall(parent)
117 tar.extractall(parent)
114
118
115 # it will be mathjax-MathJax-<sha>, rename to just mathjax
119 # it will be mathjax-MathJax-<sha>, rename to just mathjax
116 os.rename(os.path.join(parent, topdir), dest)
120 os.rename(os.path.join(parent, topdir), dest)
117
121
118 ##
122 ##
119
123
120 def extract_zip( fd, dest ) :
124 def extract_zip( fd, dest ) :
121 z = zipfile.ZipFile( fd, 'r' )
125 z = zipfile.ZipFile( fd, 'r' )
122
126
123 # we just happen to know that the first entry in the mathjax
127 # we just happen to know that the first entry in the mathjax
124 # archive is the directory that the remaining members are in.
128 # archive is the directory that the remaining members are in.
125 topdir = z.namelist()[0]
129 topdir = z.namelist()[0]
126
130
127 # extract the archive (contains a single directory) to the static/ directory
131 # extract the archive (contains a single directory) to the static/ directory
128 parent = os.path.abspath(os.path.join(dest, os.path.pardir))
132 parent = os.path.abspath(os.path.join(dest, os.path.pardir))
129 z.extractall( parent )
133 z.extractall( parent )
130
134
131 # it will be mathjax-MathJax-<sha>, rename to just mathjax
135 # it will be mathjax-MathJax-<sha>, rename to just mathjax
132 d = os.path.join(parent, topdir)
136 d = os.path.join(parent, topdir)
133 print d
137 print d
134 os.rename(os.path.join(parent, topdir), dest)
138 os.rename(os.path.join(parent, topdir), dest)
135
139
136 ##
140 ##
137
141
138 def install_mathjax(tag='v2.0', dest=default_dest, replace=False, file=None, extractor=extract_tar ):
142 def install_mathjax(tag='v2.0', dest=default_dest, replace=False, file=None, extractor=extract_tar ):
139 """Download and/or install MathJax for offline use.
143 """Download and/or install MathJax for offline use.
140
144
141 This will install mathjax to the 'static' dir in the IPython notebook
145 This will install mathjax to the 'static' dir in the IPython notebook
142 package, so it will fail if the caller does not have write access
146 package, so it will fail if the caller does not have write access
143 to that location.
147 to that location.
144
148
145 MathJax is a ~15MB download, and ~150MB installed.
149 MathJax is a ~15MB download, and ~150MB installed.
146
150
147 Parameters
151 Parameters
148 ----------
152 ----------
149
153
150 replace : bool [False]
154 replace : bool [False]
151 Whether to remove and replace an existing install.
155 Whether to remove and replace an existing install.
152 dest : str [path to default profile]
156 dest : str [path to default profile]
153 Where to locally install mathjax
157 Where to locally install mathjax
154 tag : str ['v2.0']
158 tag : str ['v2.0']
155 Which tag to download. Default is 'v2.0', the current stable release,
159 Which tag to download. Default is 'v2.0', the current stable release,
156 but alternatives include 'v1.1a' and 'master'.
160 but alternatives include 'v1.1a' and 'master'.
157 file : file like object [ defualt to content of https://github.com/mathjax/MathJax/tarball/#{tag}]
161 file : file like object [ defualt to content of https://github.com/mathjax/MathJax/tarball/#{tag}]
158 File handle from which to untar/unzip/... mathjax
162 File handle from which to untar/unzip/... mathjax
159 extractor : function
163 extractor : function
160 Method tu use to untar/unzip/... `file`
164 Method tu use to untar/unzip/... `file`
161 """
165 """
162 if not check_perms(dest, replace) :
166 if not check_perms(dest, replace) :
163 return
167 return
164
168
165 if file is None :
169 if file is None :
166 # download mathjax
170 # download mathjax
167 mathjax_url = "https://github.com/mathjax/MathJax/tarball/%s" % tag
171 mathjax_url = "https://github.com/mathjax/MathJax/tarball/%s" % tag
168 print "Downloading mathjax source from %s" % mathjax_url
172 print "Downloading mathjax source from %s" % mathjax_url
169 response = urllib2.urlopen(mathjax_url)
173 response = urllib2.urlopen(mathjax_url)
170 file = response.fp
174 file = response.fp
171
175
172 print "Extracting to %s" % dest
176 print "Extracting to %s" % dest
173 extractor( file, dest )
177 extractor( file, dest )
174
178
175 ##
179 ##
176
180
177 def test_func( remove, dest) :
181 def test_func( remove, dest) :
178 """See if mathjax appears to be installed correctly"""
182 """See if mathjax appears to be installed correctly"""
179 status = 0
183 status = 0
180 if not os.path.isdir( dest ) :
184 if not os.path.isdir( dest ) :
181 print "%s directory not found" % dest
185 print "%s directory not found" % dest
182 status = 1
186 status = 1
183 if not os.path.exists( dest + "/MathJax.js" ) :
187 if not os.path.exists( dest + "/MathJax.js" ) :
184 print "MathJax.js not present in %s" % dest
188 print "MathJax.js not present in %s" % dest
185 status = 1
189 status = 1
186 print "ok"
190 print "ok"
187 if remove and os.path.exists(dest):
191 if remove and os.path.exists(dest):
188 shutil.rmtree( dest )
192 shutil.rmtree( dest )
189 return status
193 return status
190
194
191 ##
195 ##
192
196
193 def main() :
197 def main() :
194 # This main is just simple enough that it is not worth the
198 # This main is just simple enough that it is not worth the
195 # complexity of argparse
199 # complexity of argparse
196
200
197 # What directory is mathjax in?
201 # What directory is mathjax in?
198 parser = argparse.ArgumentParser(
202 parser = argparse.ArgumentParser(
199 description="""Install mathjax from internet or local archive""",
203 description="""Install mathjax from internet or local archive""",
200 )
204 )
201
205
202 parser.add_argument(
206 parser.add_argument(
203 '-p',
207 '-p',
204 '--profile',
208 '--profile',
205 help='profile to install MathJax to. Takes precedence over install-dir option')
209 default='default',
210 help='profile to install MathJax to (default is default)')
206
211
207 parser.add_argument(
212 parser.add_argument(
208 '-i',
213 '-i',
209 '--install-dir',
214 '--install-dir',
210 default=default_dest,
215 help='custom installation directory')
211 help='installation directory (by default : %s)' % (default_dest))
212
216
213 parser.add_argument(
217 parser.add_argument(
214 '-d',
218 '-d',
215 '--dest',
219 '--dest',
216 action='store_true',
220 action='store_true',
217 help='print where current mathjax would be installed and exit')
221 help='print where current mathjax would be installed and exit')
218 parser.add_argument(
222 parser.add_argument(
219 '-r',
223 '-r',
220 '--replace',
224 '--replace',
221 action='store_true',
225 action='store_true',
222 help='Whether to replace current mathjax if it already exists')
226 help='Whether to replace current mathjax if it already exists')
223 parser.add_argument(
227 parser.add_argument(
224 '-t',
228 '-t',
225 '--test',
229 '--test',
226 action='store_true')
230 action='store_true')
227 parser.add_argument('tarball',
231 parser.add_argument('tarball',
228 help="the local tar/zip-ball containing mathjax",
232 help="the local tar/zip-ball containing mathjax",
229 nargs='?',
233 nargs='?',
230 metavar='tarball')
234 metavar='tarball')
231
235
232 pargs = parser.parse_args()
236 pargs = parser.parse_args()
233
237
234 if(pargs.profile):
238 if pargs.install_dir:
239 # Explicit install_dir overrides profile
240 dest = pargs.install_dir
241 else:
235 profile = pargs.profile
242 profile = pargs.profile
236 static = os.path.join(locate_profile(profile), 'static')
243 static = os.path.join(locate_profile(profile), 'static')
237 dest = os.path.join(static, 'mathjax')
244 dest = os.path.join(static, 'mathjax')
238 else:
239 dest = pargs.install_dir
240
241
245
242 if pargs.dest :
246 if pargs.dest :
243 print dest
247 print dest
244 return
248 return
245
249
246 # remove/replace existing mathjax?
250 # remove/replace existing mathjax?
247 if pargs.replace :
251 if pargs.replace :
248 replace = True
252 replace = True
249 else :
253 else :
250 replace = False
254 replace = False
251
255
252 # undocumented test interface
256 # undocumented test interface
253 if pargs.test :
257 if pargs.test :
254 return test_func( replace, dest)
258 return test_func( replace, dest)
255
259
256 # do it
260 # do it
257 if pargs.tarball :
261 if pargs.tarball :
258 fname = pargs.tarball
262 fname = pargs.tarball
259
263
260 # automatically detect zip/tar - could do something based
264 # automatically detect zip/tar - could do something based
261 # on file content, but really not cost-effective here.
265 # on file content, but really not cost-effective here.
262 if fname.endswith('.zip') :
266 if fname.endswith('.zip') :
263 extractor = extract_zip
267 extractor = extract_zip
264 else :
268 else :
265 extractor = extract_tar
269 extractor = extract_tar
266 # do it
270 # do it
267 install_mathjax(file=open(fname, "r"), replace=replace, extractor=extractor, dest=dest )
271 install_mathjax(file=open(fname, "r"), replace=replace, extractor=extractor, dest=dest )
268 else:
272 else:
269 install_mathjax(replace=replace, dest=dest)
273 install_mathjax(replace=replace, dest=dest)
270
274
271
275
272 if __name__ == '__main__' :
276 if __name__ == '__main__' :
273 sys.exit(main())
277 sys.exit(main())
274
278
275 __all__ = ['install_mathjax', 'main', 'dest']
279 __all__ = ['install_mathjax', 'main', 'dest']
276
280
277 """
281 """
278 Test notes:
282 Test notes:
279
283
280 IPython uses IPython.testing.iptest as a custom test controller
284 IPython uses IPython.testing.iptest as a custom test controller
281 (though it is based on nose). It might be possible to fit automatic
285 (though it is based on nose). It might be possible to fit automatic
282 tests of installation into that framework, but it looks awkward to me.
286 tests of installation into that framework, but it looks awkward to me.
283 So, here is a manual procedure for testing this automatic installer.
287 So, here is a manual procedure for testing this automatic installer.
284
288
285 Mark Sienkiewicz, 2012-08-06
289 Mark Sienkiewicz, 2012-08-06
286 first 8 letters of my last name @ stsci.edu
290 first 8 letters of my last name @ stsci.edu
287
291
288 # remove mathjax from the installed ipython instance
292 # remove mathjax from the installed ipython instance
289 # IOError ok if mathjax was never installed yet.
293 # IOError ok if mathjax was never installed yet.
290
294
291 python -m IPython.external.mathjax --test -r
295 python -m IPython.external.mathjax --test -r
292
296
293 # download and install mathjax from command line:
297 # download and install mathjax from command line:
294
298
295 python -m IPython.external.mathjax
299 python -m IPython.external.mathjax
296 python -m IPython.external.mathjax --test -r
300 python -m IPython.external.mathjax --test -r
297
301
298 # download and install from within python
302 # download and install from within python
299
303
300 python -c "from IPython.external.mathjax import install_mathjax; install_mathjax()"
304 python -c "from IPython.external.mathjax import install_mathjax; install_mathjax()"
301 python -m IPython.external.mathjax --test -r
305 python -m IPython.external.mathjax --test -r
302
306
303 # view http://www.mathjax.org/download/ in your browser
307 # view http://www.mathjax.org/download/ in your browser
304 # save-as the link for MathJax-2.0 near the bottom of the page.
308 # save-as the link for MathJax-2.0 near the bottom of the page.
305 # The file it offers is mathjax-MathJax-v2.0-20-g07669ac.zip
309 # The file it offers is mathjax-MathJax-v2.0-20-g07669ac.zip
306
310
307 python -m IPython.external.mathjax mathjax-MathJax-v2.0-20-g07669ac.zip
311 python -m IPython.external.mathjax mathjax-MathJax-v2.0-20-g07669ac.zip
308 python -m IPython.external.mathjax --test -r
312 python -m IPython.external.mathjax --test -r
309
313
310 # download https://github.com/mathjax/MathJax/tarball/v2.0 in your browser
314 # download https://github.com/mathjax/MathJax/tarball/v2.0 in your browser
311 # (this is the url used internally by install_mathjax)
315 # (this is the url used internally by install_mathjax)
312 # The file it offers is mathjax-MathJax-v2.0-20-g07669ac.tar.gz
316 # The file it offers is mathjax-MathJax-v2.0-20-g07669ac.tar.gz
313
317
314 python -m IPython.external.mathjax mathjax-MathJax-v2.0-20-g07669ac.tar.gz
318 python -m IPython.external.mathjax mathjax-MathJax-v2.0-20-g07669ac.tar.gz
315
319
316 python -m IPython.external.mathjax --test
320 python -m IPython.external.mathjax --test
317 # note no -r
321 # note no -r
318
322
319 # install it again while it is already there
323 # install it again while it is already there
320
324
321 python -m IPython.external.mathjax mathjax-MathJax-v2.0-20-g07669ac.tar.gz
325 python -m IPython.external.mathjax mathjax-MathJax-v2.0-20-g07669ac.tar.gz
322 # says "offline MathJax apparently already installed"
326 # says "offline MathJax apparently already installed"
323
327
324 python -m IPython.external.mathjax ~/mathjax-MathJax-v2.0-20-g07669ac.tar.gz
328 python -m IPython.external.mathjax ~/mathjax-MathJax-v2.0-20-g07669ac.tar.gz
325 python -m IPython.external.mathjax --test
329 python -m IPython.external.mathjax --test
326
330
327
331
328 """
332 """
General Comments 0
You need to be logged in to leave comments. Login now