From dbf3bdc6fa892896f2ef628e3db1908e165630cf 2012-03-07 22:55:13 From: Min RK Date: 2012-03-07 22:55:13 Subject: [PATCH] Merge pull request #1419 from takluyver/install-ext Add %install_ext magic function and associated extension-loading tests --- diff --git a/IPython/core/extensions.py b/IPython/core/extensions.py index 0298a8f..f7602dd 100644 --- a/IPython/core/extensions.py +++ b/IPython/core/extensions.py @@ -19,6 +19,8 @@ Authors: import os import sys +from urllib import urlretrieve +from urlparse import urlparse from IPython.config.configurable import Configurable from IPython.utils.traitlets import Instance @@ -123,3 +125,22 @@ class ExtensionManager(Configurable): def _call_unload_ipython_extension(self, mod): if hasattr(mod, 'unload_ipython_extension'): return mod.unload_ipython_extension(self.shell) + + def install_extension(self, url, filename=None): + """Download and install an IPython extension. + + If filename is given, the file will be so named (inside the extension + directory). Otherwise, the name from the URL will be used. The file must + have a .py or .zip extension; otherwise, a ValueError will be raised. + """ + # Ensure the extension directory exists + if not os.path.isdir(self.ipython_extension_dir): + os.makedirs(self.ipython_extension_dir, mode = 0777) + + if filename is None: + filename = urlparse(url).path.split('/')[-1] + if os.path.splitext(filename)[1] not in ('.py', '.zip'): + raise ValueError("The file must have a .py or .zip extension", filename) + + filename = os.path.join(self.ipython_extension_dir, filename) + return urlretrieve(url, filename) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 2f233fb..4996c20 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -3457,6 +3457,31 @@ Defaulting color scheme to 'NoColor'""" # print simple error message, rather than traceback if we can't # hook up the GUI error(str(e)) + + def magic_install_ext(self, parameter_s): + """Download and install an extension from a URL, e.g.:: + + %install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py + + The URL should point to an importable Python module - either a .py file + or a .zip file. + + Parameters: + + -n filename : Specify a name for the file, rather than taking it from + the URL. + """ + opts, args = self.parse_options(parameter_s, 'n:') + try: + filename, headers = self.extension_manager.install_extension(args, opts.get('n')) + except ValueError as e: + print e + return + + filename = os.path.basename(filename) + print "Installed %s. To use it, type:" % filename + print " %%load_ext %s" % os.path.splitext(filename)[0] + def magic_load_ext(self, module_str): """Load an IPython extension by its module name.""" diff --git a/IPython/core/tests/daft_extension.py b/IPython/core/tests/daft_extension.py new file mode 100644 index 0000000..37fefe3 --- /dev/null +++ b/IPython/core/tests/daft_extension.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +""" +Useless IPython extension to test installing and loading extensions. +""" +some_vars = {'arq': 185} + +def load_ipython_extension(ip): + # set up simplified quantity input + ip.push(some_vars) + +def unload_ipython_extension(ip): + ip.drop_by_id(some_vars) diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index dd42552..e3a2e38 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -15,6 +15,7 @@ import nose.tools as nt from IPython.testing import decorators as dec from IPython.testing import tools as tt from IPython.utils import py3compat +from IPython.utils.tempdir import TemporaryDirectory #----------------------------------------------------------------------------- # Test functions begin @@ -394,3 +395,20 @@ def test_prun_quotes(): "Test that prun does not clobber string escapes (GH #1302)" _ip.magic("prun -q x = '\t'") nt.assert_equal(_ip.user_ns['x'], '\t') + +def test_extension(): + tmpdir = TemporaryDirectory() + orig_ipython_dir = _ip.ipython_dir + try: + _ip.ipython_dir = tmpdir.name + nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension") + url = os.path.join(os.path.dirname(__file__), "daft_extension.py") + _ip.magic("install_ext %s" % url) + _ip.user_ns.pop('arq', None) + _ip.magic("load_ext daft_extension") + tt.assert_equal(_ip.user_ns['arq'], 185) + _ip.magic("unload_ext daft_extension") + assert 'arq' not in _ip.user_ns + finally: + _ip.ipython_dir = orig_ipython_dir + diff --git a/docs/source/whatsnew/development.txt b/docs/source/whatsnew/development.txt index f35c4a1..1023655 100644 --- a/docs/source/whatsnew/development.txt +++ b/docs/source/whatsnew/development.txt @@ -4,3 +4,15 @@ This document describes in-flight development work. +Redesigned IPython notebook user interface +------------------------------------------ + +.. add details + +Other new features +------------------ + +* **%install_ext**: A new magic function to install an IPython extension from + a URL. E.g. ``%install_ext https://bitbucket.org/birkenfeld/ipython-physics/raw/d1310a2ab15d/physics.py``. + +