|
|
"""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)
|
|
|
|