From cdde5bba87d7ac8f06da0273182b5503043fd306 2012-06-04 23:57:51 From: MinRK Date: 2012-06-04 23:57:51 Subject: [PATCH] add script magics Base %%script magic, and add wrappers for a few common interpreters. The list of wrapped magics is configurable. --- diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 361b7b2..dd6ed07 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2017,7 +2017,8 @@ class InteractiveShell(SingletonConfigurable): self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, m.ConfigMagics, m.DeprecatedMagics, m.ExecutionMagics, m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics, - m.NamespaceMagics, m.OSMagics, m.PylabMagics ) + m.NamespaceMagics, m.OSMagics, m.PylabMagics, m.ScriptMagics, + ) # FIXME: Move the color initialization to the DisplayHook, which # should be split into a prompt manager and displayhook. We probably diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index 23479b5..3b72f15 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -25,6 +25,7 @@ from .logging import LoggingMagics from .namespace import NamespaceMagics from .osm import OSMagics from .pylab import PylabMagics +from .script import ScriptMagics #----------------------------------------------------------------------------- # Magic implementation classes diff --git a/IPython/core/magics/script.py b/IPython/core/magics/script.py new file mode 100644 index 0000000..f00c52f --- /dev/null +++ b/IPython/core/magics/script.py @@ -0,0 +1,155 @@ +"""Magic functions for running cells in various scripts.""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 The IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import os +import re +import sys +from subprocess import Popen, PIPE + +# Our own packages +from IPython.config.configurable import Configurable +from IPython.core.error import UsageError +from IPython.core.magic import ( + Magics, magics_class, line_magic, cell_magic +) +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.process import find_cmd, FindCmdError +from IPython.utils.traitlets import List, Dict + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +@magics_class +class ScriptMagics(Magics, Configurable): + """Magics for talking to scripts + + This defines a base `%%script` cell magic for running a cell + with a program in a subprocess, and registers a few top-level + magics that call %%script with common interpreters. + """ + script_magics = List(config=True, + help="""Extra script cell magics to define + + This generates simple wrappers of `%%script foo` as `%%foo`. + + If you want to add script magics that aren't on your path, + specify them in script_paths + """, + ) + def _script_magics_default(self): + """default to a common list of programs if we find them""" + + defaults = [] + to_try = [] + if os.name == 'nt': + defaults.append('cmd') + to_try.append('powershell') + to_try.extend([ + 'sh', + 'bash', + 'perl', + 'ruby', + 'python3', + 'pypy', + ]) + + for cmd in to_try: + if cmd in self.script_paths: + defaults.append(cmd) + else: + try: + find_cmd(cmd) + except FindCmdError: + # command not found, ignore it + pass + except ImportError: + # Windows without pywin32, find_cmd doesn't work + pass + else: + defaults.append(cmd) + return defaults + + script_paths = Dict(config=True, + help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' + + Only necessary for items in script_magics where the default path will not + find the right interpreter. + """ + ) + + def __init__(self, shell=None): + Configurable.__init__(self, config=shell.config) + self._generate_script_magics() + Magics.__init__(self, shell=shell) + + def _generate_script_magics(self): + cell_magics = self.magics['cell'] + for name in self.script_magics: + cell_magics[name] = self._make_script_magic(name) + + def _make_script_magic(self, name): + """make a named magic, that calls %%script with a particular program""" + # expand to explicit path if necessary: + script = self.script_paths.get(name, name) + + def named_script_magic(line, cell): + # if line, add it as cl-flags + if line: + line = "%s %s" % (script, line) + else: + line = script + return self.shebang(line, cell) + + # write a basic docstring: + named_script_magic.__doc__ = \ + """%%{name} script magic + + Run cells with {script} in a subprocess. + + This is a shortcut for `%%script {script}` + """.format(**locals()) + + return named_script_magic + + @cell_magic("script") + def shebang(self, line, cell): + """Run a cell via a shell command + + The `%%script` line is like the #! line of script, + specifying a program (bash, perl, ruby, etc.) with which to run. + + The rest of the cell is run by that program. + + Examples + -------- + :: + + In [1]: %%script bash + ...: for i in 1 2 3; do + ...: echo $i + ...: done + 1 + 2 + 3 + """ + p = Popen(line, stdout=PIPE, stderr=PIPE, stdin=PIPE) + out,err = p.communicate(cell) + sys.stdout.write(out) + sys.stdout.flush() + sys.stderr.write(err) + sys.stderr.flush() + + # expose %%script as %%! + cell_magic('!')(shebang)