Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion IPython/core/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ def writeout_cache(self):
((?P<sep>[\-:])
((?P<endsess>~?\d+)/)?
(?P<end>\d+))?
""", re.VERBOSE)
$""", re.VERBOSE)

def extract_hist_ranges(ranges_str):
"""Turn a string of history ranges into 3-tuples of (session, start, stop).
Expand Down
43 changes: 43 additions & 0 deletions IPython/core/interactiveshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from IPython.core.history import HistoryManager
from IPython.core.inputsplitter import IPythonInputSplitter
from IPython.core.logger import Logger
from IPython.core.macro import Macro
from IPython.core.magic import Magic
from IPython.core.payload import PayloadManager
from IPython.core.plugin import PluginManager
Expand Down Expand Up @@ -2503,6 +2504,48 @@ def ask_yes_no(self,prompt,default=True):
def show_usage(self):
"""Show a usage message"""
page.page(IPython.core.usage.interactive_usage)

def find_user_code(self, target, raw=True):
"""Get a code string from history, file, or a string or macro.

This is mainly used by magic functions.

Parameters
----------
target : str
A string specifying code to retrieve. This will be tried respectively
as: ranges of input history (see %history for syntax), a filename, or
an expression evaluating to a string or Macro in the user namespace.
raw : bool
If true (default), retrieve raw history. Has no effect on the other
retrieval mechanisms.

Returns
-------
A string of code.

ValueError is raised if nothing is found, and TypeError if it evaluates
to an object of another type. In each case, .args[0] is a printable
message.
"""
code = self.extract_input_lines(target, raw=raw) # Grab history
if code:
return code
if os.path.isfile(target): # Read file
return open(target, "r").read()

try: # User namespace
codeobj = eval(target, self.user_ns)
except Exception:
raise ValueError(("'%s' was not found in history, as a file, nor in"
" the user namespace.") % target)
if isinstance(codeobj, basestring):
return codeobj
elif isinstance(codeobj, Macro):
return codeobj.value

raise TypeError("%s is neither a string nor a macro." % target,
codeobj)

#-------------------------------------------------------------------------
# Things related to IPython exiting
Expand Down
31 changes: 29 additions & 2 deletions IPython/core/macro.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@
# the file COPYING, distributed as part of this software.
#*****************************************************************************

import re
import sys

import IPython.utils.io

coding_declaration = re.compile(r"#\s*coding[:=]\s*([-\w.]+)")

class Macro(object):
"""Simple class to store the value of macros as strings.

Expand All @@ -20,9 +25,24 @@ class Macro(object):

def __init__(self,code):
"""store the macro value, as a single string which can be executed"""
self.value = code.rstrip()+'\n'

lines = []
enc = None
for line in code.splitlines():
coding_match = coding_declaration.match(line)
if coding_match:
enc = coding_match.group(1)
else:
lines.append(line)
code = "\n".join(lines)
if isinstance(code, bytes):
code = code.decode(enc or sys.getdefaultencoding())
self.value = code + '\n'

def __str__(self):
enc = sys.stdin.encoding or sys.getdefaultencoding()
return self.value.encode(enc, "replace")

def __unicode__(self):
return self.value

def __repr__(self):
Expand All @@ -31,3 +51,10 @@ def __repr__(self):
def __getstate__(self):
""" needed for safe pickling via %store """
return {'value': self.value}

def __add__(self, other):
if isinstance(other, Macro):
return Macro(self.value + other.value)
elif isinstance(other, basestring):
return Macro(self.value + other)
raise TypeError
47 changes: 33 additions & 14 deletions IPython/core/magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from cStringIO import StringIO
from getopt import getopt,GetoptError
from pprint import pformat
from xmlrpclib import ServerProxy

# cProfile was added in Python2.5
try:
Expand Down Expand Up @@ -1962,7 +1963,8 @@ def magic_time(self,parameter_s = ''):

@testdec.skip_doctest
def magic_macro(self,parameter_s = ''):
"""Define a set of input lines as a macro for future re-execution.
"""Define a macro for future re-execution. It accepts ranges of history,
filenames or string objects.

Usage:\\
%macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
Expand Down Expand Up @@ -2014,12 +2016,8 @@ def magic_macro(self,parameter_s = ''):
You can view a macro's contents by explicitly printing it with:

'print macro_name'.

For one-off cases which DON'T contain magic function calls in them you
can obtain similar results by explicitly executing slices from your
input history with:

In [60]: exec In[44:48]+In[49]"""

"""

opts,args = self.parse_options(parameter_s,'r',mode='list')
if not args: # List existing macros
Expand All @@ -2028,18 +2026,22 @@ def magic_macro(self,parameter_s = ''):
if len(args) == 1:
raise UsageError(
"%macro insufficient args; usage '%macro name n1-n2 n3-4...")
name, ranges = args[0], " ".join(args[1:])
name, codefrom = args[0], " ".join(args[1:])

#print 'rng',ranges # dbg
lines = self.extract_input_lines(ranges,'r' in opts)
try:
lines = self.shell.find_user_code(codefrom, 'r' in opts)
except (ValueError, TypeError) as e:
print e.args[0]
return
macro = Macro(lines)
self.shell.define_macro(name, macro)
print 'Macro `%s` created. To execute, type its name (without quotes).' % name
print 'Macro contents:'
print '=== Macro contents: ==='
print macro,

def magic_save(self,parameter_s = ''):
"""Save a set of lines to a given filename.
"""Save a set of lines or a macro to a given filename.

Usage:\\
%save [options] filename n1-n2 n3-n4 ... n5 .. n6 ...
Expand All @@ -2058,20 +2060,37 @@ def magic_save(self,parameter_s = ''):
it asks for confirmation before overwriting existing files."""

opts,args = self.parse_options(parameter_s,'r',mode='list')
fname,ranges = args[0], " ".join(args[1:])
fname, codefrom = args[0], " ".join(args[1:])
if not fname.endswith('.py'):
fname += '.py'
if os.path.isfile(fname):
ans = raw_input('File `%s` exists. Overwrite (y/[N])? ' % fname)
if ans.lower() not in ['y','yes']:
print 'Operation cancelled.'
return
cmds = self.extract_input_lines(ranges, 'r' in opts)
try:
cmds = self.shell.find_user_code(codefrom, 'r' in opts)
except (TypeError, ValueError) as e:
print e.args[0]
return
if isinstance(cmds, unicode):
cmds = cmds.encode("utf-8")
with open(fname,'w') as f:
f.write("# coding: utf-8\n")
f.write(cmds.encode("utf-8"))
f.write(cmds)
print 'The following commands were written to file `%s`:' % fname
print cmds

def magic_pastebin(self, parameter_s = ''):
"""Upload code to the 'Lodge it' paste bin, returning the URL."""
try:
code = self.shell.find_user_code(parameter_s)
except (ValueError, TypeError) as e:
print e.args[0]
return
pbserver = ServerProxy('http://paste.pocoo.org/xmlrpc/')
id = pbserver.pastes.newPaste("python", code)
return "http://paste.pocoo.org/show/" + id

def _edit_macro(self,mname,macro):
"""open an editor with the macro data in a file"""
Expand Down