diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index aabf035..41a6e45 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2005,7 +2005,7 @@ class InteractiveShell(SingletonConfigurable): self.register_magic_function = self.magics_manager.register_function self.define_magic = self.magics_manager.define_magic - self.register_magics(m.BasicMagics, mf.CodeMagics, mf.ConfigMagics, + self.register_magics(m.BasicMagics, m.CodeMagics, mf.ConfigMagics, mf.ExecutionMagics, mf.NamespaceMagics, mf.AutoMagics, mf.OSMagics, mf.LoggingMagics, mf.ExtensionsMagics, mf.PylabMagics, m.HistoryMagics, mf.DeprecatedMagics) diff --git a/IPython/core/magic_functions.py b/IPython/core/magic_functions.py index f1f2c5e..4c85f65 100644 --- a/IPython/core/magic_functions.py +++ b/IPython/core/magic_functions.py @@ -12,7 +12,7 @@ #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- - +# Stdlib import __builtin__ as builtin_mod import bdb import gc @@ -38,6 +38,7 @@ except ImportError: except ImportError: profile = pstats = None +# Our own packages from IPython.config.application import Application from IPython.core import debugger, oinspect from IPython.core import page @@ -59,449 +60,9 @@ from IPython.utils.terminal import set_term_title from IPython.utils.timing import clock, clock2 from IPython.utils.warn import warn, error - -# Used for exception handling in magic_edit -class MacroToEdit(ValueError): pass - - -@register_magics -class CodeMagics(Magics): - """Magics related to code management (loading, saving, editing, ...).""" - - @line_magic - def save(self, parameter_s=''): - """Save a set of lines or a macro to a given filename. - - Usage:\\ - %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ... - - Options: - - -r: use 'raw' input. By default, the 'processed' history is used, - so that magics are loaded in their transformed version to valid - Python. If this option is given, the raw input as typed as the - command line is used instead. - - This function uses the same syntax as %history for input ranges, - then saves the lines to the filename you specify. - - It adds a '.py' extension to the file if you don't do so yourself, and - it asks for confirmation before overwriting existing files.""" - - opts,args = self.parse_options(parameter_s,'r',mode='list') - fname, codefrom = unquote_filename(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 - try: - cmds = self.shell.find_user_code(codefrom, 'r' in opts) - except (TypeError, ValueError) as e: - print e.args[0] - return - with io.open(fname,'w', encoding="utf-8") as f: - f.write(u"# coding: utf-8\n") - f.write(py3compat.cast_unicode(cmds)) - print 'The following commands were written to file `%s`:' % fname - print cmds - - @line_magic - def pastebin(self, parameter_s=''): - """Upload code to Github's Gist paste bin, returning the URL. - - Usage:\\ - %pastebin [-d "Custom description"] 1-7 - - The argument can be an input history range, a filename, or the name of a - string or macro. - - Options: - - -d: Pass a custom description for the gist. The default will say - "Pasted from IPython". - """ - opts, args = self.parse_options(parameter_s, 'd:') - - try: - code = self.shell.find_user_code(args) - except (ValueError, TypeError) as e: - print e.args[0] - return - - post_data = json.dumps({ - "description": opts.get('d', "Pasted from IPython"), - "public": True, - "files": { - "file1.py": { - "content": code - } - } - }).encode('utf-8') - - response = urlopen("https://api.github.com/gists", post_data) - response_data = json.loads(response.read().decode('utf-8')) - return response_data['html_url'] - - @line_magic - def loadpy(self, arg_s): - """Load a .py python script into the GUI console. - - This magic command can either take a local filename or a url:: - - %loadpy myscript.py - %loadpy http://www.example.com/myscript.py - """ - arg_s = unquote_filename(arg_s) - remote_url = arg_s.startswith(('http://', 'https://')) - local_url = not remote_url - if local_url and not arg_s.endswith('.py'): - # Local files must be .py; for remote URLs it's possible that the - # fetch URL doesn't have a .py in it (many servers have an opaque - # URL, such as scipy-central.org). - raise ValueError('%%loadpy only works with .py files: %s' % arg_s) - - # openpy takes care of finding the source encoding (per PEP 263) - if remote_url: - contents = openpy.read_py_url(arg_s, skip_encoding_cookie=True) - else: - contents = openpy.read_py_file(arg_s, skip_encoding_cookie=True) - - self.shell.set_next_input(contents) - - def _find_edit_target(self, args, opts, last_call): - """Utility method used by magic_edit to find what to edit.""" - - def make_filename(arg): - "Make a filename from the given args" - arg = unquote_filename(arg) - try: - filename = get_py_filename(arg) - except IOError: - # If it ends with .py but doesn't already exist, assume we want - # a new file. - if arg.endswith('.py'): - filename = arg - else: - filename = None - return filename - - # Set a few locals from the options for convenience: - opts_prev = 'p' in opts - opts_raw = 'r' in opts - - # custom exceptions - class DataIsObject(Exception): pass - - # Default line number value - lineno = opts.get('n',None) - - if opts_prev: - args = '_%s' % last_call[0] - if not self.shell.user_ns.has_key(args): - args = last_call[1] - - # use last_call to remember the state of the previous call, but don't - # let it be clobbered by successive '-p' calls. - try: - last_call[0] = self.shell.displayhook.prompt_count - if not opts_prev: - last_call[1] = args - except: - pass - - # by default this is done with temp files, except when the given - # arg is a filename - use_temp = True - - data = '' - - # First, see if the arguments should be a filename. - filename = make_filename(args) - if filename: - use_temp = False - elif args: - # Mode where user specifies ranges of lines, like in %macro. - data = self.shell.extract_input_lines(args, opts_raw) - if not data: - try: - # Load the parameter given as a variable. If not a string, - # process it as an object instead (below) - - #print '*** args',args,'type',type(args) # dbg - data = eval(args, self.shell.user_ns) - if not isinstance(data, basestring): - raise DataIsObject - - except (NameError,SyntaxError): - # given argument is not a variable, try as a filename - filename = make_filename(args) - if filename is None: - warn("Argument given (%s) can't be found as a variable " - "or as a filename." % args) - return - use_temp = False - - except DataIsObject: - # macros have a special edit function - if isinstance(data, Macro): - raise MacroToEdit(data) - - # For objects, try to edit the file where they are defined - try: - filename = inspect.getabsfile(data) - if 'fakemodule' in filename.lower() and \ - inspect.isclass(data): - # class created by %edit? Try to find source - # by looking for method definitions instead, the - # __module__ in those classes is FakeModule. - attrs = [getattr(data, aname) for aname in dir(data)] - for attr in attrs: - if not inspect.ismethod(attr): - continue - filename = inspect.getabsfile(attr) - if filename and \ - 'fakemodule' not in filename.lower(): - # change the attribute to be the edit - # target instead - data = attr - break - - datafile = 1 - except TypeError: - filename = make_filename(args) - datafile = 1 - warn('Could not find file where `%s` is defined.\n' - 'Opening a file named `%s`' % (args,filename)) - # Now, make sure we can actually read the source (if it was - # in a temp file it's gone by now). - if datafile: - try: - if lineno is None: - lineno = inspect.getsourcelines(data)[1] - except IOError: - filename = make_filename(args) - if filename is None: - warn('The file `%s` where `%s` was defined cannot ' - 'be read.' % (filename,data)) - return - use_temp = False - - if use_temp: - filename = self.shell.mktempfile(data) - print 'IPython will make a temporary file named:',filename - - return filename, lineno, use_temp - - def _edit_macro(self,mname,macro): - """open an editor with the macro data in a file""" - filename = self.shell.mktempfile(macro.value) - self.shell.hooks.editor(filename) - - # and make a new macro object, to replace the old one - mfile = open(filename) - mvalue = mfile.read() - mfile.close() - self.shell.user_ns[mname] = Macro(mvalue) - - @line_magic - def ed(self, parameter_s=''): - """Alias to %edit.""" - return self.edit(parameter_s) - - @skip_doctest - @line_magic - def edit(self, parameter_s='',last_call=['','']): - """Bring up an editor and execute the resulting code. - - Usage: - %edit [options] [args] - - %edit runs IPython's editor hook. The default version of this hook is - set to call the editor specified by your $EDITOR environment variable. - If this isn't found, it will default to vi under Linux/Unix and to - notepad under Windows. See the end of this docstring for how to change - the editor hook. - - You can also set the value of this editor via the - ``TerminalInteractiveShell.editor`` option in your configuration file. - This is useful if you wish to use a different editor from your typical - default with IPython (and for Windows users who typically don't set - environment variables). - - This command allows you to conveniently edit multi-line code right in - your IPython session. - - If called without arguments, %edit opens up an empty editor with a - temporary file and will execute the contents of this file when you - close it (don't forget to save it!). - - - Options: - - -n : open the editor at a specified line number. By default, - the IPython editor hook uses the unix syntax 'editor +N filename', but - you can configure this by providing your own modified hook if your - favorite editor supports line-number specifications with a different - syntax. - - -p: this will call the editor with the same data as the previous time - it was used, regardless of how long ago (in your current session) it - was. - - -r: use 'raw' input. This option only applies to input taken from the - user's history. By default, the 'processed' history is used, so that - magics are loaded in their transformed version to valid Python. If - this option is given, the raw input as typed as the command line is - used instead. When you exit the editor, it will be executed by - IPython's own processor. - - -x: do not execute the edited code immediately upon exit. This is - mainly useful if you are editing programs which need to be called with - command line arguments, which you can then do using %run. - - - Arguments: - - If arguments are given, the following possibilities exist: - - - If the argument is a filename, IPython will load that into the - editor. It will execute its contents with execfile() when you exit, - loading any code in the file into your interactive namespace. - - - The arguments are ranges of input history, e.g. "7 ~1/4-6". - The syntax is the same as in the %history magic. - - - If the argument is a string variable, its contents are loaded - into the editor. You can thus edit any string which contains - python code (including the result of previous edits). - - - If the argument is the name of an object (other than a string), - IPython will try to locate the file where it was defined and open the - editor at the point where it is defined. You can use `%edit function` - to load an editor exactly at the point where 'function' is defined, - edit it and have the file be executed automatically. - - - If the object is a macro (see %macro for details), this opens up your - specified editor with a temporary file containing the macro's data. - Upon exit, the macro is reloaded with the contents of the file. - - Note: opening at an exact line is only supported under Unix, and some - editors (like kedit and gedit up to Gnome 2.8) do not understand the - '+NUMBER' parameter necessary for this feature. Good editors like - (X)Emacs, vi, jed, pico and joe all do. - - After executing your code, %edit will return as output the code you - typed in the editor (except when it was an existing file). This way - you can reload the code in further invocations of %edit as a variable, - via _ or Out[], where is the prompt number of - the output. - - Note that %edit is also available through the alias %ed. - - This is an example of creating a simple function inside the editor and - then modifying it. First, start up the editor:: - - In [1]: ed - Editing... done. Executing edited code... - Out[1]: 'def foo():\\n print "foo() was defined in an editing - session"\\n' - - We can then call the function foo():: - - In [2]: foo() - foo() was defined in an editing session - - Now we edit foo. IPython automatically loads the editor with the - (temporary) file where foo() was previously defined:: - - In [3]: ed foo - Editing... done. Executing edited code... - - And if we call foo() again we get the modified version:: - - In [4]: foo() - foo() has now been changed! - - Here is an example of how to edit a code snippet successive - times. First we call the editor:: - - In [5]: ed - Editing... done. Executing edited code... - hello - Out[5]: "print 'hello'\\n" - - Now we call it again with the previous output (stored in _):: - - In [6]: ed _ - Editing... done. Executing edited code... - hello world - Out[6]: "print 'hello world'\\n" - - Now we call it with the output #8 (stored in _8, also as Out[8]):: - - In [7]: ed _8 - Editing... done. Executing edited code... - hello again - Out[7]: "print 'hello again'\\n" - - - Changing the default editor hook: - - If you wish to write your own editor hook, you can put it in a - configuration file which you load at startup time. The default hook - is defined in the IPython.core.hooks module, and you can use that as a - starting example for further modifications. That file also has - general instructions on how to set a new hook for use once you've - defined it.""" - opts,args = self.parse_options(parameter_s,'prxn:') - - try: - filename, lineno, is_temp = self._find_edit_target(args, opts, last_call) - except MacroToEdit as e: - self._edit_macro(args, e.args[0]) - return - - # do actual editing here - print 'Editing...', - sys.stdout.flush() - try: - # Quote filenames that may have spaces in them - if ' ' in filename: - filename = "'%s'" % filename - self.shell.hooks.editor(filename,lineno) - except TryNext: - warn('Could not open editor') - return - - # XXX TODO: should this be generalized for all string vars? - # For now, this is special-cased to blocks created by cpaste - if args.strip() == 'pasted_block': - self.shell.user_ns['pasted_block'] = file_read(filename) - - if 'x' in opts: # -x prevents actual execution - print - else: - print 'done. Executing edited code...' - if 'r' in opts: # Untranslated IPython code - self.shell.run_cell(file_read(filename), - store_history=False) - else: - self.shell.safe_execfile(filename, self.shell.user_ns, - self.shell.user_ns) - - if is_temp: - try: - return open(filename).read() - except IOError,msg: - if msg.filename == filename: - warn('File not found. Did you forget to save?') - return - else: - self.shell.showtraceback() - +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- @register_magics class ConfigMagics(Magics): diff --git a/IPython/core/magics/__init__.py b/IPython/core/magics/__init__.py index d51657e..528a1e9 100644 --- a/IPython/core/magics/__init__.py +++ b/IPython/core/magics/__init__.py @@ -13,8 +13,9 @@ #----------------------------------------------------------------------------- from IPython.core.magic import Magics, register_magics -from basic import BasicMagics -from history import HistoryMagics +from .basic import BasicMagics +from .code import CodeMagics, MacroToEdit +from .history import HistoryMagics #----------------------------------------------------------------------------- # Magic implementation classes diff --git a/IPython/core/magics/code.py b/IPython/core/magics/code.py new file mode 100644 index 0000000..767211c --- /dev/null +++ b/IPython/core/magics/code.py @@ -0,0 +1,478 @@ +"""Implementation of basic magic functions. +""" +#----------------------------------------------------------------------------- +# 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 inspect +import io +import json +import os +import sys +from urllib2 import urlopen + +# Our own packages +from IPython.core.error import TryNext +from IPython.core.macro import Macro +from IPython.core.magic import Magics, register_magics, line_magic +from IPython.testing.skipdoctest import skip_doctest +from IPython.utils import openpy +from IPython.utils import py3compat +from IPython.utils.io import file_read +from IPython.utils.path import get_py_filename, unquote_filename +from IPython.utils.warn import warn + +#----------------------------------------------------------------------------- +# Magic implementation classes +#----------------------------------------------------------------------------- + +# Used for exception handling in magic_edit +class MacroToEdit(ValueError): pass + + +@register_magics +class CodeMagics(Magics): + """Magics related to code management (loading, saving, editing, ...).""" + + @line_magic + def save(self, parameter_s=''): + """Save a set of lines or a macro to a given filename. + + Usage:\\ + %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ... + + Options: + + -r: use 'raw' input. By default, the 'processed' history is used, + so that magics are loaded in their transformed version to valid + Python. If this option is given, the raw input as typed as the + command line is used instead. + + This function uses the same syntax as %history for input ranges, + then saves the lines to the filename you specify. + + It adds a '.py' extension to the file if you don't do so yourself, and + it asks for confirmation before overwriting existing files.""" + + opts,args = self.parse_options(parameter_s,'r',mode='list') + fname, codefrom = unquote_filename(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 + try: + cmds = self.shell.find_user_code(codefrom, 'r' in opts) + except (TypeError, ValueError) as e: + print e.args[0] + return + with io.open(fname,'w', encoding="utf-8") as f: + f.write(u"# coding: utf-8\n") + f.write(py3compat.cast_unicode(cmds)) + print 'The following commands were written to file `%s`:' % fname + print cmds + + @line_magic + def pastebin(self, parameter_s=''): + """Upload code to Github's Gist paste bin, returning the URL. + + Usage:\\ + %pastebin [-d "Custom description"] 1-7 + + The argument can be an input history range, a filename, or the name of a + string or macro. + + Options: + + -d: Pass a custom description for the gist. The default will say + "Pasted from IPython". + """ + opts, args = self.parse_options(parameter_s, 'd:') + + try: + code = self.shell.find_user_code(args) + except (ValueError, TypeError) as e: + print e.args[0] + return + + post_data = json.dumps({ + "description": opts.get('d', "Pasted from IPython"), + "public": True, + "files": { + "file1.py": { + "content": code + } + } + }).encode('utf-8') + + response = urlopen("https://api.github.com/gists", post_data) + response_data = json.loads(response.read().decode('utf-8')) + return response_data['html_url'] + + @line_magic + def loadpy(self, arg_s): + """Load a .py python script into the GUI console. + + This magic command can either take a local filename or a url:: + + %loadpy myscript.py + %loadpy http://www.example.com/myscript.py + """ + arg_s = unquote_filename(arg_s) + remote_url = arg_s.startswith(('http://', 'https://')) + local_url = not remote_url + if local_url and not arg_s.endswith('.py'): + # Local files must be .py; for remote URLs it's possible that the + # fetch URL doesn't have a .py in it (many servers have an opaque + # URL, such as scipy-central.org). + raise ValueError('%%loadpy only works with .py files: %s' % arg_s) + + # openpy takes care of finding the source encoding (per PEP 263) + if remote_url: + contents = openpy.read_py_url(arg_s, skip_encoding_cookie=True) + else: + contents = openpy.read_py_file(arg_s, skip_encoding_cookie=True) + + self.shell.set_next_input(contents) + + def _find_edit_target(self, args, opts, last_call): + """Utility method used by magic_edit to find what to edit.""" + + def make_filename(arg): + "Make a filename from the given args" + arg = unquote_filename(arg) + try: + filename = get_py_filename(arg) + except IOError: + # If it ends with .py but doesn't already exist, assume we want + # a new file. + if arg.endswith('.py'): + filename = arg + else: + filename = None + return filename + + # Set a few locals from the options for convenience: + opts_prev = 'p' in opts + opts_raw = 'r' in opts + + # custom exceptions + class DataIsObject(Exception): pass + + # Default line number value + lineno = opts.get('n',None) + + if opts_prev: + args = '_%s' % last_call[0] + if not self.shell.user_ns.has_key(args): + args = last_call[1] + + # use last_call to remember the state of the previous call, but don't + # let it be clobbered by successive '-p' calls. + try: + last_call[0] = self.shell.displayhook.prompt_count + if not opts_prev: + last_call[1] = args + except: + pass + + # by default this is done with temp files, except when the given + # arg is a filename + use_temp = True + + data = '' + + # First, see if the arguments should be a filename. + filename = make_filename(args) + if filename: + use_temp = False + elif args: + # Mode where user specifies ranges of lines, like in %macro. + data = self.shell.extract_input_lines(args, opts_raw) + if not data: + try: + # Load the parameter given as a variable. If not a string, + # process it as an object instead (below) + + #print '*** args',args,'type',type(args) # dbg + data = eval(args, self.shell.user_ns) + if not isinstance(data, basestring): + raise DataIsObject + + except (NameError,SyntaxError): + # given argument is not a variable, try as a filename + filename = make_filename(args) + if filename is None: + warn("Argument given (%s) can't be found as a variable " + "or as a filename." % args) + return + use_temp = False + + except DataIsObject: + # macros have a special edit function + if isinstance(data, Macro): + raise MacroToEdit(data) + + # For objects, try to edit the file where they are defined + try: + filename = inspect.getabsfile(data) + if 'fakemodule' in filename.lower() and \ + inspect.isclass(data): + # class created by %edit? Try to find source + # by looking for method definitions instead, the + # __module__ in those classes is FakeModule. + attrs = [getattr(data, aname) for aname in dir(data)] + for attr in attrs: + if not inspect.ismethod(attr): + continue + filename = inspect.getabsfile(attr) + if filename and \ + 'fakemodule' not in filename.lower(): + # change the attribute to be the edit + # target instead + data = attr + break + + datafile = 1 + except TypeError: + filename = make_filename(args) + datafile = 1 + warn('Could not find file where `%s` is defined.\n' + 'Opening a file named `%s`' % (args,filename)) + # Now, make sure we can actually read the source (if it was + # in a temp file it's gone by now). + if datafile: + try: + if lineno is None: + lineno = inspect.getsourcelines(data)[1] + except IOError: + filename = make_filename(args) + if filename is None: + warn('The file `%s` where `%s` was defined cannot ' + 'be read.' % (filename,data)) + return + use_temp = False + + if use_temp: + filename = self.shell.mktempfile(data) + print 'IPython will make a temporary file named:',filename + + return filename, lineno, use_temp + + def _edit_macro(self,mname,macro): + """open an editor with the macro data in a file""" + filename = self.shell.mktempfile(macro.value) + self.shell.hooks.editor(filename) + + # and make a new macro object, to replace the old one + mfile = open(filename) + mvalue = mfile.read() + mfile.close() + self.shell.user_ns[mname] = Macro(mvalue) + + @line_magic + def ed(self, parameter_s=''): + """Alias to %edit.""" + return self.edit(parameter_s) + + @skip_doctest + @line_magic + def edit(self, parameter_s='',last_call=['','']): + """Bring up an editor and execute the resulting code. + + Usage: + %edit [options] [args] + + %edit runs IPython's editor hook. The default version of this hook is + set to call the editor specified by your $EDITOR environment variable. + If this isn't found, it will default to vi under Linux/Unix and to + notepad under Windows. See the end of this docstring for how to change + the editor hook. + + You can also set the value of this editor via the + ``TerminalInteractiveShell.editor`` option in your configuration file. + This is useful if you wish to use a different editor from your typical + default with IPython (and for Windows users who typically don't set + environment variables). + + This command allows you to conveniently edit multi-line code right in + your IPython session. + + If called without arguments, %edit opens up an empty editor with a + temporary file and will execute the contents of this file when you + close it (don't forget to save it!). + + + Options: + + -n : open the editor at a specified line number. By default, + the IPython editor hook uses the unix syntax 'editor +N filename', but + you can configure this by providing your own modified hook if your + favorite editor supports line-number specifications with a different + syntax. + + -p: this will call the editor with the same data as the previous time + it was used, regardless of how long ago (in your current session) it + was. + + -r: use 'raw' input. This option only applies to input taken from the + user's history. By default, the 'processed' history is used, so that + magics are loaded in their transformed version to valid Python. If + this option is given, the raw input as typed as the command line is + used instead. When you exit the editor, it will be executed by + IPython's own processor. + + -x: do not execute the edited code immediately upon exit. This is + mainly useful if you are editing programs which need to be called with + command line arguments, which you can then do using %run. + + + Arguments: + + If arguments are given, the following possibilities exist: + + - If the argument is a filename, IPython will load that into the + editor. It will execute its contents with execfile() when you exit, + loading any code in the file into your interactive namespace. + + - The arguments are ranges of input history, e.g. "7 ~1/4-6". + The syntax is the same as in the %history magic. + + - If the argument is a string variable, its contents are loaded + into the editor. You can thus edit any string which contains + python code (including the result of previous edits). + + - If the argument is the name of an object (other than a string), + IPython will try to locate the file where it was defined and open the + editor at the point where it is defined. You can use `%edit function` + to load an editor exactly at the point where 'function' is defined, + edit it and have the file be executed automatically. + + - If the object is a macro (see %macro for details), this opens up your + specified editor with a temporary file containing the macro's data. + Upon exit, the macro is reloaded with the contents of the file. + + Note: opening at an exact line is only supported under Unix, and some + editors (like kedit and gedit up to Gnome 2.8) do not understand the + '+NUMBER' parameter necessary for this feature. Good editors like + (X)Emacs, vi, jed, pico and joe all do. + + After executing your code, %edit will return as output the code you + typed in the editor (except when it was an existing file). This way + you can reload the code in further invocations of %edit as a variable, + via _ or Out[], where is the prompt number of + the output. + + Note that %edit is also available through the alias %ed. + + This is an example of creating a simple function inside the editor and + then modifying it. First, start up the editor:: + + In [1]: ed + Editing... done. Executing edited code... + Out[1]: 'def foo():\\n print "foo() was defined in an editing + session"\\n' + + We can then call the function foo():: + + In [2]: foo() + foo() was defined in an editing session + + Now we edit foo. IPython automatically loads the editor with the + (temporary) file where foo() was previously defined:: + + In [3]: ed foo + Editing... done. Executing edited code... + + And if we call foo() again we get the modified version:: + + In [4]: foo() + foo() has now been changed! + + Here is an example of how to edit a code snippet successive + times. First we call the editor:: + + In [5]: ed + Editing... done. Executing edited code... + hello + Out[5]: "print 'hello'\\n" + + Now we call it again with the previous output (stored in _):: + + In [6]: ed _ + Editing... done. Executing edited code... + hello world + Out[6]: "print 'hello world'\\n" + + Now we call it with the output #8 (stored in _8, also as Out[8]):: + + In [7]: ed _8 + Editing... done. Executing edited code... + hello again + Out[7]: "print 'hello again'\\n" + + + Changing the default editor hook: + + If you wish to write your own editor hook, you can put it in a + configuration file which you load at startup time. The default hook + is defined in the IPython.core.hooks module, and you can use that as a + starting example for further modifications. That file also has + general instructions on how to set a new hook for use once you've + defined it.""" + opts,args = self.parse_options(parameter_s,'prxn:') + + try: + filename, lineno, is_temp = self._find_edit_target(args, opts, last_call) + except MacroToEdit as e: + self._edit_macro(args, e.args[0]) + return + + # do actual editing here + print 'Editing...', + sys.stdout.flush() + try: + # Quote filenames that may have spaces in them + if ' ' in filename: + filename = "'%s'" % filename + self.shell.hooks.editor(filename,lineno) + except TryNext: + warn('Could not open editor') + return + + # XXX TODO: should this be generalized for all string vars? + # For now, this is special-cased to blocks created by cpaste + if args.strip() == 'pasted_block': + self.shell.user_ns['pasted_block'] = file_read(filename) + + if 'x' in opts: # -x prevents actual execution + print + else: + print 'done. Executing edited code...' + if 'r' in opts: # Untranslated IPython code + self.shell.run_cell(file_read(filename), + store_history=False) + else: + self.shell.safe_execfile(filename, self.shell.user_ns, + self.shell.user_ns) + + if is_temp: + try: + return open(filename).read() + except IOError,msg: + if msg.filename == filename: + warn('File not found. Did you forget to save?') + return + else: + self.shell.showtraceback()