add IPython.html.nbextensions.install_nbextension

for installing files into IPYTHONDIR/nbextensions
pull/37/head
MinRK 12 years ago
parent 47abe842b2
commit 704b349302

@ -0,0 +1,101 @@
# coding: utf-8
"""Utilities for installing Javascript extensions for the notebook"""
#-----------------------------------------------------------------------------
# Copyright (C) 2014 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.
#-----------------------------------------------------------------------------
from __future__ import print_function
import os
import shutil
from os.path import basename, join as pjoin
from IPython.utils.path import get_ipython_dir
from IPython.utils.py3compat import string_types, cast_unicode_py2
def _should_copy(src, dest, verbose=1):
"""should a file be copied?"""
if not os.path.exists(dest):
return True
if os.stat(dest).st_mtime < os.stat(src).st_mtime:
if verbose >= 2:
print("%s is out of date" % dest)
return True
if verbose >= 2:
print("%s is up to date" % dest)
return False
def _maybe_copy(src, dest, verbose=1):
"""copy a file if it needs updating"""
if _should_copy(src, dest, verbose):
if verbose >= 1:
print("copying %s -> %s" % (src, dest))
shutil.copy2(src, dest)
def install_nbextension(files, overwrite=False, ipython_dir=None, verbose=1):
"""Install a Javascript extension for the notebook
Stages files and/or directories into IPYTHONDIR/nbextensions.
By default, this comparse modification time, and only stages files that need updating.
If `overwrite` is specified, matching files are purged before proceeding.
Parameters
----------
files : list(paths)
One or more paths to existing files or directories to install.
These will be installed with their base name, so '/path/to/foo'
will install to 'nbextensions/foo'.
overwrite : bool [default: False]
If True, always install the files, regardless of what may already be installed.
ipython_dir : str [optional]
The path to an IPython directory, if the default value is not desired.
get_ipython_dir() is used by default.
verbose : int [default: 1]
Set verbosity level. The default is 1, where file actions are printed.
set verbose=2 for more output, or verbose=0 for silence.
"""
ipython_dir = ipython_dir or get_ipython_dir()
nbext = pjoin(ipython_dir, u'nbextensions')
# make sure nbextensions dir exists
if not os.path.exists(nbext):
os.makedirs(nbext)
if isinstance(files, string_types):
# one file given, turn it into a list
files = [files]
for path in map(cast_unicode_py2, files):
dest = pjoin(nbext, basename(path))
if overwrite and os.path.exists(dest):
if verbose >= 1:
print("removing %s" % dest)
if os.path.isdir(dest):
shutil.rmtree(dest)
else:
os.remove(dest)
if os.path.isdir(path):
strip_prefix_len = len(path) - len(basename(path))
for parent, dirs, files in os.walk(path):
dest_dir = pjoin(nbext, parent[strip_prefix_len:])
if not os.path.exists(dest_dir):
if verbose >= 2:
print("making directory %s" % dest_dir)
os.makedirs(dest_dir)
for file in files:
src = pjoin(parent, file)
# print("%r, %r" % (dest_dir, file))
dest = pjoin(dest_dir, file)
_maybe_copy(src, dest, verbose)
else:
src = path
_maybe_copy(src, dest, verbose)

@ -0,0 +1,201 @@
# coding: utf-8
"""Test installation of notebook extensions"""
#-----------------------------------------------------------------------------
# Copyright (C) 2014 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.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
import glob
import os
import re
import time
from contextlib import contextmanager
from os.path import basename, join as pjoin
from unittest import TestCase
import nose.tools as nt
from IPython.external.decorator import decorator
import IPython.testing.tools as tt
import IPython.utils.path
from IPython.utils import py3compat
from IPython.utils.tempdir import TemporaryDirectory
from IPython.html import nbextensions
from IPython.html.nbextensions import install_nbextension
#-----------------------------------------------------------------------------
# Test functions
#-----------------------------------------------------------------------------
def touch(file, mtime=None):
"""ensure a file exists, and set its modification time
returns the modification time of the file
"""
open(file, 'a').close()
# set explicit mtime
if mtime:
atime = os.stat(file).st_atime
os.utime(file, (atime, mtime))
return os.stat(file).st_mtime
class TestInstallNBExtension(TestCase):
def tempdir(self):
td = TemporaryDirectory()
self.tempdirs.append(td)
return py3compat.cast_unicode(td.name)
def setUp(self):
self.tempdirs = []
src = self.src = self.tempdir()
self.files = files = [
pjoin(u'ƒile'),
pjoin(u'∂ir', u'ƒile1'),
pjoin(u'∂ir', u'∂ir2', u'ƒile2'),
]
for file in files:
fullpath = os.path.join(self.src, file)
parent = os.path.dirname(fullpath)
if not os.path.exists(parent):
os.makedirs(parent)
touch(fullpath)
self.ipdir = self.tempdir()
self.save_get_ipython_dir = nbextensions.get_ipython_dir
nbextensions.get_ipython_dir = lambda : self.ipdir
def tearDown(self):
for td in self.tempdirs:
td.cleanup()
nbextensions.get_ipython_dir = self.save_get_ipython_dir
def assert_path_exists(self, path):
if not os.path.exists(path):
self.fail(u"%s should exist" % path)
def assert_not_path_exists(self, path):
if os.path.exists(path):
self.fail(u"%s should not exist" % path)
def assert_installed(self, relative_path, ipdir=None):
self.assert_path_exists(
pjoin(ipdir or self.ipdir, u'nbextensions', relative_path)
)
def assert_not_installed(self, relative_path, ipdir=None):
self.assert_not_path_exists(
pjoin(ipdir or self.ipdir, u'nbextensions', relative_path)
)
def test_create_ipython_dir(self):
"""install_nbextension when ipython_dir doesn't exist"""
with TemporaryDirectory() as td:
ipdir = pjoin(td, u'ipython')
install_nbextension(self.src, ipython_dir=ipdir)
self.assert_path_exists(ipdir)
for file in self.files:
self.assert_installed(
pjoin(basename(self.src), file),
ipdir
)
def test_create_nbextensions(self):
with TemporaryDirectory() as ipdir:
install_nbextension(self.src, ipython_dir=ipdir)
self.assert_installed(
pjoin(basename(self.src), u'ƒile'),
ipdir
)
def test_single_file(self):
file = self.files[0]
install_nbextension(pjoin(self.src, file))
self.assert_installed(file)
def test_single_dir(self):
d = u'∂ir'
install_nbextension(pjoin(self.src, d))
self.assert_installed(self.files[-1])
def test_install_nbextension(self):
install_nbextension(glob.glob(pjoin(self.src, '*')))
for file in self.files:
self.assert_installed(file)
def test_overwrite_file(self):
with TemporaryDirectory() as d:
fname = u'ƒ.js'
src = pjoin(d, fname)
with open(src, 'w') as f:
f.write('first')
mtime = touch(src)
dest = pjoin(self.ipdir, u'nbextensions', fname)
install_nbextension(src)
with open(src, 'w') as f:
f.write('overwrite')
mtime = touch(src, mtime - 100)
install_nbextension(src, overwrite=True)
with open(dest) as f:
self.assertEqual(f.read(), 'overwrite')
def test_overwrite_dir(self):
with TemporaryDirectory() as src:
# src = py3compat.cast_unicode_py2(src)
base = basename(src)
fname = u'ƒ.js'
touch(pjoin(src, fname))
install_nbextension(src)
self.assert_installed(pjoin(base, fname))
os.remove(pjoin(src, fname))
fname2 = u'∂.js'
touch(pjoin(src, fname2))
install_nbextension(src, overwrite=True)
self.assert_installed(pjoin(base, fname2))
self.assert_not_installed(pjoin(base, fname))
def test_update_file(self):
with TemporaryDirectory() as d:
fname = u'ƒ.js'
src = pjoin(d, fname)
with open(src, 'w') as f:
f.write('first')
mtime = touch(src)
install_nbextension(src)
self.assert_installed(fname)
dest = pjoin(self.ipdir, u'nbextensions', fname)
old_mtime = os.stat(dest).st_mtime
with open(src, 'w') as f:
f.write('overwrite')
touch(src, mtime + 10)
install_nbextension(src)
with open(dest) as f:
self.assertEqual(f.read(), 'overwrite')
def test_skip_old_file(self):
with TemporaryDirectory() as d:
fname = u'ƒ.js'
src = pjoin(d, fname)
mtime = touch(src)
install_nbextension(src)
self.assert_installed(fname)
dest = pjoin(self.ipdir, u'nbextensions', fname)
old_mtime = os.stat(dest).st_mtime
mtime = touch(src, mtime - 100)
install_nbextension(src)
new_mtime = os.stat(dest).st_mtime
self.assertEqual(new_mtime, old_mtime)
def test_quiet(self):
with tt.AssertNotPrints(re.compile(r'.+')):
install_nbextension(self.src, verbose=0)
Loading…
Cancel
Save