From c679a6cc4163e9d556e234c995c5d1110985f236 2013-01-10 07:10:23 From: Min RK Date: 2013-01-10 07:10:23 Subject: [PATCH] Merge pull request #2476 from minrk/editmagic allow %edit to work for some interactively defined objects interactively defined classes still don't work --- diff --git a/IPython/core/magics/code.py b/IPython/core/magics/code.py index e1f66a6..30b4b96 100644 --- a/IPython/core/magics/code.py +++ b/IPython/core/magics/code.py @@ -17,6 +17,7 @@ import inspect import io import json import os +import re import sys from urllib2 import urlopen @@ -39,6 +40,13 @@ from IPython.utils.warn import warn # Used for exception handling in magic_edit class MacroToEdit(ValueError): pass +ipython_input_pat = re.compile(r"$") + +class InteractivelyDefined(Exception): + """Exception for interactively defined variable in magic_edit""" + def __init__(self, index): + self.index = index + @magics_class class CodeMagics(Magics): @@ -274,7 +282,7 @@ class CodeMagics(Magics): if filename is None: warn("Argument given (%s) can't be found as a variable " "or as a filename." % args) - return + return (None, None, None) use_temp = False except DataIsObject: @@ -301,12 +309,18 @@ class CodeMagics(Magics): # target instead data = attr break - + + m = ipython_input_pat.match(os.path.basename(filename)) + if m: + raise InteractivelyDefined(int(m.groups()[0])) + datafile = 1 if filename is None: filename = make_filename(args) datafile = 1 - warn('Could not find file where `%s` is defined.\n' + if filename is not None: + # only warn about this if we get a real name + 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). @@ -316,9 +330,9 @@ class CodeMagics(Magics): if lineno is None: filename = make_filename(args) if filename is None: - warn('The file `%s` where `%s` was defined ' - 'cannot be read.' % (filename, data)) - return + warn('The file where `%s` was defined ' + 'cannot be read or found.' % data) + return (None, None, None) use_temp = False if use_temp: @@ -491,6 +505,15 @@ class CodeMagics(Magics): except MacroToEdit as e: self._edit_macro(args, e.args[0]) return + except InteractivelyDefined as e: + print "Editing In[%i]" % e.index + args = str(e.index) + filename, lineno, is_temp = self._find_edit_target(self.shell, + args, opts, last_call) + if filename is None: + # nothing was found, warnings have already been issued, + # just give up. + return # do actual editing here print 'Editing...', diff --git a/IPython/core/oinspect.py b/IPython/core/oinspect.py index ba5f935..a1dda16 100644 --- a/IPython/core/oinspect.py +++ b/IPython/core/oinspect.py @@ -296,7 +296,7 @@ def find_file(obj): pass except: pass - return fname + return cast_unicode(fname) def find_source_lines(obj): @@ -326,6 +326,8 @@ def find_source_lines(obj): # For instances, try the class object like getsource() does if hasattr(obj, '__class__'): lineno = inspect.getsourcelines(obj.__class__)[1] + else: + lineno = None except: return None diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index ae6e211..fafdbf9 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -28,7 +28,7 @@ from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic, line_cell_magic, register_line_magic, register_cell_magic, register_line_cell_magic) -from IPython.core.magics import execution, script +from IPython.core.magics import execution, script, code from IPython.nbformat.v3.tests.nbexamples import nb0 from IPython.nbformat import current from IPython.testing import decorators as dec @@ -792,3 +792,49 @@ def test_store(): ip.user_ns['var'] = 39 ip.run_line_magic('store' , '-r') nt.assert_equal(ip.user_ns['var'], 39) + + +def _run_edit_test(arg_s, exp_filename=None, + exp_lineno=-1, + exp_contents=None, + exp_is_temp=None): + ip = get_ipython() + M = code.CodeMagics(ip) + last_call = ['',''] + opts,args = M.parse_options(arg_s,'prxn:') + filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call) + + if exp_filename is not None: + nt.assert_equal(exp_filename, filename) + if exp_contents is not None: + with io.open(filename, 'r') as f: + contents = f.read() + nt.assert_equal(exp_contents, contents) + if exp_lineno != -1: + nt.assert_equal(exp_lineno, lineno) + if exp_is_temp is not None: + nt.assert_equal(exp_is_temp, is_temp) + + +def test_edit_interactive(): + """%edit on interactively defined objects""" + ip = get_ipython() + n = ip.execution_count + ip.run_cell(u"def foo(): return 1", store_history=True) + + try: + _run_edit_test("foo") + except code.InteractivelyDefined as e: + nt.assert_equal(e.index, n) + else: + nt.fail("Should have raised InteractivelyDefined") + + +def test_edit_cell(): + """%edit [cell id]""" + ip = get_ipython() + + ip.run_cell(u"def foo(): return 1", store_history=True) + + # test + _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)