magic.py
359 lines
| 12.6 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r2498 | # encoding: utf-8 | ||
Ville M. Vainio
|
r1032 | """Magic functions for InteractiveShell. | ||
Fernando Perez
|
r1853 | """ | ||
Ville M. Vainio
|
r1032 | |||
Brian Granger
|
r2498 | #----------------------------------------------------------------------------- | ||
# Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and | ||||
Fernando Perez
|
r6917 | # Copyright (C) 2001 Fernando Perez <fperez@colorado.edu> | ||
# Copyright (C) 2008 The IPython Development Team | ||||
Brian Granger
|
r2498 | |||
Ville M. Vainio
|
r1032 | # Distributed under the terms of the BSD License. The full license is in | ||
# the file COPYING, distributed as part of this software. | ||||
Brian Granger
|
r2498 | #----------------------------------------------------------------------------- | ||
Ville M. Vainio
|
r1032 | |||
Brian Granger
|
r2498 | #----------------------------------------------------------------------------- | ||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r6919 | # Stdlib | ||
Ville M. Vainio
|
r1032 | import os | ||
import re | ||||
Fernando Perez
|
r6917 | import sys | ||
Fernando Perez
|
r6923 | import types | ||
Fernando Perez
|
r6919 | from getopt import getopt, GetoptError | ||
# Our own | ||||
Fernando Perez
|
r6917 | from IPython.config.configurable import Configurable | ||
Fernando Perez
|
r6919 | from IPython.core import oinspect | ||
Fernando Perez
|
r2363 | from IPython.core.error import UsageError | ||
Brian Granger
|
r2245 | from IPython.core.prefilter import ESC_MAGIC | ||
Fernando Perez
|
r6919 | from IPython.external.decorator import decorator | ||
Fernando Perez
|
r6917 | from IPython.utils.ipstruct import Struct | ||
Fernando Perez
|
r6919 | from IPython.utils.process import arg_split | ||
Fernando Perez
|
r6922 | from IPython.utils.traitlets import Bool, Dict, Instance | ||
Fernando Perez
|
r6919 | from IPython.utils.warn import error | ||
#----------------------------------------------------------------------------- | ||||
# Globals | ||||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r6922 | |||
# A dict we'll use for each class that has magics, used as temporary storage to | ||||
# pass information between the @line/cell_magic method decorators and the | ||||
# @register_magics class decorator, because the method decorators have no | ||||
# access to the class when they run. See for more details: | ||||
# http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class | ||||
Fernando Perez
|
r6923 | magics = dict(line={}, cell={}) | ||
magic_types = ('line', 'cell') | ||||
Fernando Perez
|
r2363 | |||
Brian Granger
|
r2498 | #----------------------------------------------------------------------------- | ||
Fernando Perez
|
r6917 | # Utility classes and functions | ||
Brian Granger
|
r2498 | #----------------------------------------------------------------------------- | ||
Fernando Perez
|
r6917 | class Bunch: pass | ||
Ville M. Vainio
|
r1032 | def on_off(tag): | ||
"""Return an ON/OFF string for a 1/0 input. Simple utility function.""" | ||||
return ['OFF','ON'][tag] | ||||
def compress_dhist(dh): | ||||
head, tail = dh[:-10], dh[-10:] | ||||
newhead = [] | ||||
Fernando Perez
|
r1860 | done = set() | ||
Ville M. Vainio
|
r1032 | for h in head: | ||
if h in done: | ||||
continue | ||||
newhead.append(h) | ||||
done.add(h) | ||||
Bernardo B. Marques
|
r4872 | return newhead + tail | ||
Fernando Perez
|
r2358 | |||
Fernando Perez
|
r6917 | |||
Thomas Kluyver
|
r3479 | def needs_local_scope(func): | ||
"""Decorator to mark magic functions which need to local scope to run.""" | ||||
func.needs_local_scope = True | ||||
return func | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r6919 | #----------------------------------------------------------------------------- | ||
# Class and method decorators for registering magics | ||||
#----------------------------------------------------------------------------- | ||||
def register_magics(cls): | ||||
cls.registered = True | ||||
Fernando Perez
|
r6923 | cls.magics = dict(line = magics['line'], | ||
cell = magics['cell']) | ||||
magics['line'] = {} | ||||
magics['cell'] = {} | ||||
Fernando Perez
|
r6919 | return cls | ||
Fernando Perez
|
r6923 | def validate_type(magic_type): | ||
if magic_type not in magic_types: | ||||
raise ValueError('magic_type must be one of %s, %s given' % | ||||
magic_types, magic_type) | ||||
Fernando Perez
|
r6919 | |||
Fernando Perez
|
r6922 | |||
Fernando Perez
|
r6923 | def _magic_marker(magic_type): | ||
validate_type(magic_type) | ||||
Fernando Perez
|
r6919 | |||
# This is a closure to capture the magic_type. We could also use a class, | ||||
# but it's overkill for just that one bit of state. | ||||
def magic_deco(arg): | ||||
call = lambda f, *a, **k: f(*a, **k) | ||||
if callable(arg): | ||||
# "Naked" decorator call (just @foo, no args) | ||||
func = arg | ||||
name = func.func_name | ||||
func.magic_name = name | ||||
retval = decorator(call, func) | ||||
elif isinstance(arg, basestring): | ||||
# Decorator called with arguments (@foo('bar')) | ||||
name = arg | ||||
def mark(func, *a, **kw): | ||||
func.magic_name = name | ||||
return decorator(call, func) | ||||
retval = mark | ||||
else: | ||||
raise ValueError("Decorator can only be called with " | ||||
"string or function") | ||||
# Record the magic function in the global table that will then be | ||||
# appended to the class via the register_magics class decorator | ||||
Fernando Perez
|
r6923 | #print 'magics:', magics # dbg | ||
Fernando Perez
|
r6922 | magics[magic_type][name] = retval | ||
Fernando Perez
|
r6919 | |||
return retval | ||||
return magic_deco | ||||
line_magic = _magic_marker('line') | ||||
cell_magic = _magic_marker('cell') | ||||
#----------------------------------------------------------------------------- | ||||
# Core Magic classes | ||||
#----------------------------------------------------------------------------- | ||||
Ville M. Vainio
|
r1032 | |||
Fernando Perez
|
r6923 | class MagicsManager(Configurable): | ||
Fernando Perez
|
r6917 | """Object that handles all magic-related functionality for IPython. | ||
""" | ||||
Fernando Perez
|
r6921 | # Non-configurable class attributes | ||
Fernando Perez
|
r6922 | magics = Dict | ||
Fernando Perez
|
r6921 | |||
Fernando Perez
|
r6917 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') | ||
Ville M. Vainio
|
r1032 | |||
Fernando Perez
|
r6922 | auto_magic = Bool | ||
_auto_status = [ | ||||
Fernando Perez
|
r6917 | 'Automagic is OFF, % prefix IS needed for magic functions.', | ||
Fernando Perez
|
r6922 | 'Automagic is ON, % prefix IS NOT needed for magic functions.'] | ||
MinRK
|
r5225 | |||
Fernando Perez
|
r6923 | user_magics = Instance('IPython.core.magic_functions.UserMagics') | ||
Fernando Perez
|
r6910 | |||
Fernando Perez
|
r6923 | def __init__(self, shell=None, config=None, user_magics=None, **traits): | ||
super(MagicsManager, self).__init__(shell=shell, config=config, | ||||
user_magics=user_magics, **traits) | ||||
Ville M. Vainio
|
r1032 | |||
Fernando Perez
|
r6922 | def auto_status(self): | ||
"""Return descriptive string with automagic status.""" | ||||
return self._auto_status[self.auto_magic] | ||||
Ville M. Vainio
|
r1032 | |||
def lsmagic(self): | ||||
Fernando Perez
|
r6921 | """Return a dict of currently available magic functions. | ||
The return dict has the keys 'line' and 'cell', corresponding to the | ||||
two types of magics we support. Each value is a list of names. | ||||
""" | ||||
Fernando Perez
|
r6922 | return self.magics | ||
Fernando Perez
|
r6921 | |||
def register(self, *magics): | ||||
"""Register one or more instances of Magics. | ||||
""" | ||||
# Start by validating them to ensure they have all had their magic | ||||
# methods registered at the instance level | ||||
for m in magics: | ||||
if not m.registered: | ||||
raise ValueError("Class of magics %r was constructed without " | ||||
"the @register_macics class decorator") | ||||
Fernando Perez
|
r6923 | if type(m) is type: | ||
# If we're given an uninstantiated class | ||||
m = m(self.shell) | ||||
Fernando Perez
|
r6922 | self.magics.update(m.magics) | ||
Fernando Perez
|
r6921 | |||
Fernando Perez
|
r6923 | def define_magic(self, magic_name, func, magic_type='line'): | ||
"""Expose own function as magic function for ipython | ||||
Example:: | ||||
def foo_impl(self, parameter_s=''): | ||||
'My very own magic!. (Use docstrings, IPython reads them).' | ||||
print 'Magic function. Passed parameter is between < >:' | ||||
print '<%s>' % parameter_s | ||||
print 'The self object is:', self | ||||
ip.define_magic('foo', foo_impl) | ||||
""" | ||||
# Create the new method in the user_magics and register it in the | ||||
# global table | ||||
self.user_magics.new_magic(magic_name, func, magic_type) | ||||
self.magics[magic_type][magic_name] = \ | ||||
self.user_magics.magics[magic_type][magic_name] | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r6919 | # Key base class that provides the central functionality for magics. | ||
Fernando Perez
|
r6917 | |||
Fernando Perez
|
r6919 | class Magics(object): | ||
Fernando Perez
|
r6917 | """Base class for implementing magic functions. | ||
Shell functions which can be reached as %function_name. All magic | ||||
functions should accept a string, which they can parse for their own | ||||
needs. This can make some functions easier to type, eg `%cd ../` | ||||
vs. `%cd("../")` | ||||
Fernando Perez
|
r6919 | |||
Classes providing magic functions need to subclass this class, and they | ||||
MUST: | ||||
- Use the method decorators `@line_magic` and `@cell_magic` to decorate | ||||
individual methods as magic functions, AND | ||||
- Use the class decorator `@register_magics` to ensure that the magic | ||||
methods are properly registered at the instance level upon instance | ||||
initialization. | ||||
See :mod:`magic_functions` for examples of actual implementation classes. | ||||
Fernando Perez
|
r6917 | """ | ||
Fernando Perez
|
r6923 | # Dict holding all command-line options for each magic. | ||
options_table = None | ||||
Fernando Perez
|
r6917 | |||
Fernando Perez
|
r6922 | # Non-configurable class attributes | ||
magics = Dict | ||||
Fernando Perez
|
r6917 | class __metaclass__(type): | ||
def __new__(cls, name, bases, dct): | ||||
cls.registered = False | ||||
return type.__new__(cls, name, bases, dct) | ||||
def __init__(self, shell): | ||||
if not(self.__class__.registered): | ||||
raise ValueError('unregistered Magics') | ||||
self.shell = shell | ||||
Ville M. Vainio
|
r1032 | def arg_err(self,func): | ||
"""Print docstring if incorrect arguments were passed""" | ||||
print 'Error in arguments:' | ||||
Brian Granger
|
r2498 | print oinspect.getdoc(func) | ||
Ville M. Vainio
|
r1032 | |||
def format_latex(self,strng): | ||||
"""Format a string for latex inclusion.""" | ||||
# Characters that need to be escaped for latex: | ||||
escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE) | ||||
# Magic command names as headers: | ||||
Brian Granger
|
r2245 | cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC, | ||
Ville M. Vainio
|
r1032 | re.MULTILINE) | ||
Bernardo B. Marques
|
r4872 | # Magic commands | ||
Brian Granger
|
r2245 | cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC, | ||
Ville M. Vainio
|
r1032 | re.MULTILINE) | ||
# Paragraph continue | ||||
par_re = re.compile(r'\\$',re.MULTILINE) | ||||
# The "\n" symbol | ||||
newline_re = re.compile(r'\\n') | ||||
# Now build the string for output: | ||||
#strng = cmd_name_re.sub(r'\n\\texttt{\\textsl{\\large \1}}:',strng) | ||||
strng = cmd_name_re.sub(r'\n\\bigskip\n\\texttt{\\textbf{ \1}}:', | ||||
strng) | ||||
strng = cmd_re.sub(r'\\texttt{\g<cmd>}',strng) | ||||
strng = par_re.sub(r'\\\\',strng) | ||||
strng = escape_re.sub(r'\\\1',strng) | ||||
strng = newline_re.sub(r'\\textbackslash{}n',strng) | ||||
return strng | ||||
Fernando Perez
|
r6917 | def parse_options(self, arg_str, opt_str, *long_opts, **kw): | ||
Ville M. Vainio
|
r1032 | """Parse options passed to an argument string. | ||
The interface is similar to that of getopt(), but it returns back a | ||||
Struct with the options as keys and the stripped argument string still | ||||
as a string. | ||||
arg_str is quoted as a true sys.argv vector by using shlex.split. | ||||
This allows us to easily expand variables, glob files, quote | ||||
arguments, etc. | ||||
Options: | ||||
-mode: default 'string'. If given as 'list', the argument string is | ||||
returned as a list (split on whitespace) instead of a string. | ||||
-list_all: put all option values in lists. Normally only options | ||||
appearing more than once are put in a list. | ||||
-posix (True): whether to split the input line in POSIX mode or not, | ||||
as per the conventions outlined in the shlex module from the | ||||
standard library.""" | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | # inject default options at the beginning of the input line | ||
caller = sys._getframe(1).f_code.co_name.replace('magic_','') | ||||
arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str) | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | mode = kw.get('mode','string') | ||
if mode not in ['string','list']: | ||||
raise ValueError,'incorrect mode given: %s' % mode | ||||
# Get options | ||||
list_all = kw.get('list_all',0) | ||||
Fernando Perez
|
r2450 | posix = kw.get('posix', os.name == 'posix') | ||
MinRK
|
r5672 | strict = kw.get('strict', True) | ||
Ville M. Vainio
|
r1032 | |||
# Check if we have more than one argument to warrant extra processing: | ||||
odict = {} # Dictionary with options | ||||
args = arg_str.split() | ||||
if len(args) >= 1: | ||||
# If the list of inputs only has 0 or 1 thing in it, there's no | ||||
# need to look for options | ||||
MinRK
|
r5672 | argv = arg_split(arg_str, posix, strict) | ||
Ville M. Vainio
|
r1032 | # Do regular option processing | ||
try: | ||||
opts,args = getopt(argv,opt_str,*long_opts) | ||||
except GetoptError,e: | ||||
Bernardo B. Marques
|
r4872 | raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str, | ||
Ville M. Vainio
|
r1032 | " ".join(long_opts))) | ||
for o,a in opts: | ||||
if o.startswith('--'): | ||||
o = o[2:] | ||||
else: | ||||
o = o[1:] | ||||
try: | ||||
odict[o].append(a) | ||||
except AttributeError: | ||||
odict[o] = [odict[o],a] | ||||
except KeyError: | ||||
if list_all: | ||||
odict[o] = [a] | ||||
else: | ||||
odict[o] = a | ||||
# Prepare opts,args for return | ||||
opts = Struct(odict) | ||||
if mode == 'string': | ||||
args = ' '.join(args) | ||||
return opts,args | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r6923 | def default_option(self, fn, optstr): | ||
Fernando Perez
|
r6917 | """Make an entry in the options_table for fn, with value optstr""" | ||
if fn not in self.lsmagic(): | ||||
error("%s is not a magic function" % fn) | ||||
self.options_table[fn] = optstr | ||||
Fernando Perez
|
r6923 | |||
def new_magic(self, magic_name, func, magic_type='line'): | ||||
"""TODO | ||||
""" | ||||
validate_type(magic_type) | ||||
meth = types.MethodType(func, self) | ||||
setattr(self, magic_name, meth) | ||||
self.magics[magic_type][magic_name] = meth | ||||