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