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