mathjax.py
315 lines
| 9.5 KiB
| text/x-python
|
PythonLexer
Matthias BUSSONNIER
|
r8682 | #!/usr/bin/python | ||
MinRK
|
r4652 | """Utility function for installing MathJax javascript library into | ||
the notebook's 'static' directory, for offline use. | ||||
Matthias BUSSONNIER
|
r8507 | Authors: | ||
* Min RK | ||||
* Mark Sienkiewicz | ||||
* Matthias Bussonnier | ||||
Mark Sienkiewicz at STScI
|
r8154 | To download and install MathJax: | ||
From Python: | ||||
>>> from IPython.external.mathjax import install_mathjax | ||||
>>> install_mathjax() | ||||
From the command line: | ||||
$ python -m IPython.external.mathjax | ||||
To install MathJax from a file you have already downloaded: | ||||
$ python -m IPython.external.mathjax mathjax-xxx.tar.gz | ||||
$ python -m IPython.external.mathjax mathjax-xxx.zip | ||||
It will not install MathJax if it is already there. Use -r to | ||||
replace the existing copy of MathJax. | ||||
To find the directory where IPython would like MathJax installed: | ||||
$ python -m IPython.external.mathjax -d | ||||
MinRK
|
r4652 | |||
""" | ||||
Matthias BUSSONNIER
|
r8682 | |||
MinRK
|
r4652 | #----------------------------------------------------------------------------- | ||
# Copyright (C) 2008-2011 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
Mark Sienkiewicz at STScI
|
r8154 | |||
MinRK
|
r4652 | #----------------------------------------------------------------------------- | ||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
import os | ||||
import shutil | ||||
Mark Sienkiewicz at STScI
|
r8154 | import sys | ||
MinRK
|
r4652 | import tarfile | ||
Mark Sienkiewicz at STScI
|
r8154 | import urllib2 | ||
import zipfile | ||||
MinRK
|
r4652 | |||
Matthias BUSSONNIER
|
r8682 | |||
Matthias BUSSONNIER
|
r8670 | from IPython.utils.path import locate_profile | ||
Matthias BUSSONNIER
|
r8682 | from IPython.external import argparse | ||
MinRK
|
r4652 | #----------------------------------------------------------------------------- | ||
Mark Sienkiewicz at STScI
|
r8154 | # | ||
MinRK
|
r4652 | #----------------------------------------------------------------------------- | ||
Mark Sienkiewicz at STScI
|
r8154 | # Where mathjax will be installed. | ||
Matthias BUSSONNIER
|
r8682 | static = os.path.join(locate_profile('default'), 'static') | ||
default_dest = os.path.join(static, 'mathjax') | ||||
Mark Sienkiewicz at STScI
|
r8154 | |||
## | ||||
# Test for access to install mathjax. | ||||
Matthias BUSSONNIER
|
r8682 | def check_perms(dest, replace=False): | ||
parent = os.path.abspath(os.path.join(dest, os.path.pardir)) | ||||
Bussonnier Matthias
|
r8942 | components = dest.split(os.path.sep) | ||
subpaths = [ os.path.sep+os.path.sep.join(components[1:i]) for i in range(1,len(components))] | ||||
existing_path = filter(os.path.exists, subpaths) | ||||
last_writable = existing_path[-1] | ||||
if not os.access(last_writable, os.W_OK): | ||||
Matthias BUSSONNIER
|
r8682 | raise IOError("Need have write access to %s" % parent) | ||
Bussonnier Matthias
|
r8942 | not_existing = [ path for path in subpaths if path not in existing_path] | ||
# subfolder we will create, will obviously be writable | ||||
# should we still considere checking separately that | ||||
# ipython profiles have been created ? | ||||
for folder in not_existing: | ||||
os.mkdir(folder) | ||||
MinRK
|
r4652 | if os.path.exists(dest): | ||
if replace: | ||||
if not os.access(dest, os.W_OK): | ||||
Matthias BUSSONNIER
|
r8507 | raise IOError("Need have write access to %s" % dest) | ||
MinRK
|
r4652 | print "removing previous MathJax install" | ||
shutil.rmtree(dest) | ||||
Mark Sienkiewicz at STScI
|
r8154 | return True | ||
MinRK
|
r4652 | else: | ||
print "offline MathJax apparently already installed" | ||||
Mark Sienkiewicz at STScI
|
r8154 | return False | ||
else : | ||||
return True | ||||
## | ||||
def extract_tar( fd, dest ) : | ||||
MinRK
|
r4652 | # use 'r|gz' stream mode, because socket file-like objects can't seek: | ||
Mark Sienkiewicz at STScI
|
r8154 | tar = tarfile.open(fileobj=fd, mode='r|gz') | ||
# we just happen to know that the first entry in the mathjax | ||||
# archive is the directory that the remaining members are in. | ||||
MinRK
|
r4652 | topdir = tar.firstmember.path | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
# extract the archive (contains a single directory) to the static/ directory | ||||
Matthias BUSSONNIER
|
r8682 | parent = os.path.abspath(os.path.join(dest, os.path.pardir)) | ||
tar.extractall(parent) | ||||
Mark Sienkiewicz at STScI
|
r8154 | |||
MinRK
|
r4652 | # it will be mathjax-MathJax-<sha>, rename to just mathjax | ||
Matthias BUSSONNIER
|
r8682 | os.rename(os.path.join(parent, topdir), dest) | ||
MinRK
|
r4652 | |||
Mark Sienkiewicz at STScI
|
r8154 | ## | ||
def extract_zip( fd, dest ) : | ||||
z = zipfile.ZipFile( fd, 'r' ) | ||||
# we just happen to know that the first entry in the mathjax | ||||
# archive is the directory that the remaining members are in. | ||||
topdir = z.namelist()[0] | ||||
# extract the archive (contains a single directory) to the static/ directory | ||||
Matthias BUSSONNIER
|
r8682 | parent = os.path.abspath(os.path.join(dest, os.path.pardir)) | ||
z.extractall( parent ) | ||||
Mark Sienkiewicz at STScI
|
r8154 | |||
# it will be mathjax-MathJax-<sha>, rename to just mathjax | ||||
Matthias BUSSONNIER
|
r8682 | d = os.path.join(parent, topdir) | ||
Mark Sienkiewicz at STScI
|
r8154 | print d | ||
Matthias BUSSONNIER
|
r8682 | os.rename(os.path.join(parent, topdir), dest) | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
## | ||||
Matthias BUSSONNIER
|
r8671 | def install_mathjax(tag='v2.0', dest=default_dest, replace=False, file=None, extractor=extract_tar ): | ||
Matthias BUSSONNIER
|
r8507 | """Download and/or install MathJax for offline use. | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
This will install mathjax to the 'static' dir in the IPython notebook | ||||
package, so it will fail if the caller does not have write access | ||||
to that location. | ||||
MathJax is a ~15MB download, and ~150MB installed. | ||||
Parameters | ||||
---------- | ||||
replace : bool [False] | ||||
Whether to remove and replace an existing install. | ||||
Matthias BUSSONNIER
|
r8671 | dest : str [path to default profile] | ||
Where to locally install mathjax | ||||
Matthias BUSSONNIER
|
r8507 | tag : str ['v2.0'] | ||
Matthias BUSSONNIER
|
r8670 | Which tag to download. Default is 'v2.0', the current stable release, | ||
Mark Sienkiewicz at STScI
|
r8154 | but alternatives include 'v1.1a' and 'master'. | ||
Matthias BUSSONNIER
|
r8507 | file : file like object [ defualt to content of https://github.com/mathjax/MathJax/tarball/#{tag}] | ||
File handle from which to untar/unzip/... mathjax | ||||
extractor : function | ||||
Method tu use to untar/unzip/... `file` | ||||
Mark Sienkiewicz at STScI
|
r8154 | """ | ||
Matthias BUSSONNIER
|
r8682 | if not check_perms(dest, replace) : | ||
Mark Sienkiewicz at STScI
|
r8154 | return | ||
Matthias BUSSONNIER
|
r8507 | if file is None : | ||
Mark Sienkiewicz at STScI
|
r8154 | # download mathjax | ||
Matthias BUSSONNIER
|
r8682 | mathjax_url = "https://github.com/mathjax/MathJax/tarball/%s" % tag | ||
print "Downloading mathjax source from %s" % mathjax_url | ||||
Mark Sienkiewicz at STScI
|
r8154 | response = urllib2.urlopen(mathjax_url) | ||
Matthias BUSSONNIER
|
r8507 | file = response.fp | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
Matthias BUSSONNIER
|
r8682 | print "Extracting to %s" % dest | ||
extractor( file, dest ) | ||||
Mark Sienkiewicz at STScI
|
r8154 | |||
## | ||||
Matthias BUSSONNIER
|
r8682 | def test_func( remove, dest) : | ||
Mark Sienkiewicz at STScI
|
r8154 | """See if mathjax appears to be installed correctly""" | ||
Matthias BUSSONNIER
|
r8682 | status = 0 | ||
Mark Sienkiewicz at STScI
|
r8154 | if not os.path.isdir( dest ) : | ||
Matthias BUSSONNIER
|
r8682 | print "%s directory not found" % dest | ||
status = 1 | ||||
Mark Sienkiewicz at STScI
|
r8154 | if not os.path.exists( dest + "/MathJax.js" ) : | ||
Matthias BUSSONNIER
|
r8682 | print "MathJax.js not present in %s" % dest | ||
status = 1 | ||||
Mark Sienkiewicz at STScI
|
r8154 | print "ok" | ||
Matthias BUSSONNIER
|
r8682 | if remove and os.path.exists(dest): | ||
Mark Sienkiewicz at STScI
|
r8154 | shutil.rmtree( dest ) | ||
Matthias BUSSONNIER
|
r8507 | return status | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
## | ||||
Matthias BUSSONNIER
|
r8682 | def main() : | ||
Mark Sienkiewicz at STScI
|
r8154 | # This main is just simple enough that it is not worth the | ||
# complexity of argparse | ||||
# What directory is mathjax in? | ||||
Matthias BUSSONNIER
|
r8682 | parser = argparse.ArgumentParser( | ||
description="""Install mathjax from internet or local archive""", | ||||
) | ||||
parser.add_argument( | ||||
'-i', | ||||
'--install-dir', | ||||
default=default_dest, | ||||
help='installation directory (by default : %s)' % (default_dest)) | ||||
parser.add_argument( | ||||
'-d', | ||||
'--dest', | ||||
action='store_true', | ||||
help='print where is current mathjax would be installed and exit') | ||||
parser.add_argument( | ||||
'-r', | ||||
'--replace', | ||||
action='store_true', | ||||
help='Wether to replace current mathjax if already exist') | ||||
parser.add_argument( | ||||
'-t', | ||||
'--test', | ||||
action='store_true') | ||||
parser.add_argument('tarball', | ||||
help="the local tar/zip-ball containing mathjax", | ||||
nargs='?', | ||||
metavar='tarball') | ||||
pargs = parser.parse_args() | ||||
dest = pargs.install_dir | ||||
if pargs.dest : | ||||
Mark Sienkiewicz at STScI
|
r8154 | print dest | ||
return | ||||
# remove/replace existing mathjax? | ||||
Matthias BUSSONNIER
|
r8682 | if pargs.replace : | ||
Mark Sienkiewicz at STScI
|
r8154 | replace = True | ||
else : | ||||
replace = False | ||||
# undocumented test interface | ||||
Matthias BUSSONNIER
|
r8682 | if pargs.test : | ||
return test_func( replace, dest) | ||||
Mark Sienkiewicz at STScI
|
r8154 | |||
Matthias BUSSONNIER
|
r8507 | # do it | ||
Matthias BUSSONNIER
|
r8682 | if pargs.tarball : | ||
fname = pargs.tarball | ||||
Mark Sienkiewicz at STScI
|
r8154 | |||
# automatically detect zip/tar - could do something based | ||||
Matthias BUSSONNIER
|
r8682 | # on file content, but really not cost-effective here. | ||
Mark Sienkiewicz at STScI
|
r8154 | if fname.endswith('.zip') : | ||
extractor = extract_zip | ||||
else : | ||||
extractor = extract_tar | ||||
# do it | ||||
Matthias BUSSONNIER
|
r8682 | install_mathjax(file=open(fname, "r"), replace=replace, extractor=extractor, dest=dest ) | ||
else: | ||||
install_mathjax(replace=replace, dest=dest) | ||||
Mark Sienkiewicz at STScI
|
r8154 | |||
if __name__ == '__main__' : | ||||
Matthias BUSSONNIER
|
r8682 | sys.exit(main()) | ||
MinRK
|
r4652 | |||
Matthias BUSSONNIER
|
r8682 | __all__ = ['install_mathjax', 'main', 'dest'] | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
""" | ||||
Test notes: | ||||
IPython uses IPython.testing.iptest as a custom test controller | ||||
(though it is based on nose). It might be possible to fit automatic | ||||
tests of installation into that framework, but it looks awkward to me. | ||||
So, here is a manual procedure for testing this automatic installer. | ||||
Mark Sienkiewicz, 2012-08-06 | ||||
first 8 letters of my last name @ stsci.edu | ||||
# remove mathjax from the installed ipython instance | ||||
# IOError ok if mathjax was never installed yet. | ||||
Matthias BUSSONNIER
|
r8682 | python -m IPython.external.mathjax --test -r | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
# download and install mathjax from command line: | ||||
Matthias BUSSONNIER
|
r8507 | python -m IPython.external.mathjax | ||
Matthias BUSSONNIER
|
r8682 | python -m IPython.external.mathjax --test -r | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
# download and install from within python | ||||
python -c "from IPython.external.mathjax import install_mathjax; install_mathjax()" | ||||
Matthias BUSSONNIER
|
r8682 | python -m IPython.external.mathjax --test -r | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
# view http://www.mathjax.org/download/ in your browser | ||||
Matthias BUSSONNIER
|
r8670 | # save-as the link for MathJax-2.0 near the bottom of the page. | ||
# The file it offers is mathjax-MathJax-v2.0-20-g07669ac.zip | ||||
Mark Sienkiewicz at STScI
|
r8154 | |||
Matthias BUSSONNIER
|
r8670 | python -m IPython.external.mathjax mathjax-MathJax-v2.0-20-g07669ac.zip | ||
Matthias BUSSONNIER
|
r8682 | python -m IPython.external.mathjax --test -r | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
Matthias BUSSONNIER
|
r8670 | # download https://github.com/mathjax/MathJax/tarball/v2.0 in your browser | ||
Mark Sienkiewicz at STScI
|
r8154 | # (this is the url used internally by install_mathjax) | ||
Matthias BUSSONNIER
|
r8670 | # The file it offers is mathjax-MathJax-v2.0-20-g07669ac.tar.gz | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
Matthias BUSSONNIER
|
r8670 | python -m IPython.external.mathjax mathjax-MathJax-v2.0-20-g07669ac.tar.gz | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
Matthias BUSSONNIER
|
r8682 | python -m IPython.external.mathjax --test | ||
Matthias BUSSONNIER
|
r8507 | # note no -r | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
# install it again while it is already there | ||||
Matthias BUSSONNIER
|
r8670 | python -m IPython.external.mathjax mathjax-MathJax-v2.0-20-g07669ac.tar.gz | ||
Mark Sienkiewicz at STScI
|
r8154 | # says "offline MathJax apparently already installed" | ||
Matthias BUSSONNIER
|
r8670 | python -m IPython.external.mathjax ~/mathjax-MathJax-v2.0-20-g07669ac.tar.gz | ||
Matthias BUSSONNIER
|
r8682 | python -m IPython.external.mathjax --test | ||
Mark Sienkiewicz at STScI
|
r8154 | |||
""" | ||||