##// END OF EJS Templates
Refactor code used by %edit for better sharing....
Fernando Perez -
r3899:4ccf70cf merge
parent child Browse files
Show More
@@ -89,6 +89,9 b' def needs_local_scope(func):'
89 """Decorator to mark magic functions which need to local scope to run."""
89 """Decorator to mark magic functions which need to local scope to run."""
90 func.needs_local_scope = True
90 func.needs_local_scope = True
91 return func
91 return func
92
93 # Used for exception handling in magic_edit
94 class MacroToEdit(ValueError): pass
92
95
93 #***************************************************************************
96 #***************************************************************************
94 # Main class implementing Magic functionality
97 # Main class implementing Magic functionality
@@ -2020,7 +2023,6 b' Currently the magic system has the following functions:\\n"""'
2020 'print macro_name'.
2023 'print macro_name'.
2021
2024
2022 """
2025 """
2023
2024 opts,args = self.parse_options(parameter_s,'r',mode='list')
2026 opts,args = self.parse_options(parameter_s,'r',mode='list')
2025 if not args: # List existing macros
2027 if not args: # List existing macros
2026 return sorted(k for k,v in self.shell.user_ns.iteritems() if\
2028 return sorted(k for k,v in self.shell.user_ns.iteritems() if\
@@ -2111,6 +2113,126 b' Currently the magic system has the following functions:\\n"""'
2111 else:
2113 else:
2112 content = open(arg_s).read()
2114 content = open(arg_s).read()
2113 self.set_next_input(content)
2115 self.set_next_input(content)
2116
2117 def _find_edit_target(self, args, opts, last_call):
2118 """Utility method used by magic_edit to find what to edit."""
2119
2120 def make_filename(arg):
2121 "Make a filename from the given args"
2122 try:
2123 filename = get_py_filename(arg)
2124 except IOError:
2125 # If it ends with .py but doesn't already exist, assume we want
2126 # a new file.
2127 if args.endswith('.py'):
2128 filename = arg
2129 else:
2130 filename = None
2131 return filename
2132
2133 # Set a few locals from the options for convenience:
2134 opts_prev = 'p' in opts
2135 opts_raw = 'r' in opts
2136
2137 # custom exceptions
2138 class DataIsObject(Exception): pass
2139
2140 # Default line number value
2141 lineno = opts.get('n',None)
2142
2143 if opts_prev:
2144 args = '_%s' % last_call[0]
2145 if not self.shell.user_ns.has_key(args):
2146 args = last_call[1]
2147
2148 # use last_call to remember the state of the previous call, but don't
2149 # let it be clobbered by successive '-p' calls.
2150 try:
2151 last_call[0] = self.shell.displayhook.prompt_count
2152 if not opts_prev:
2153 last_call[1] = parameter_s
2154 except:
2155 pass
2156
2157 # by default this is done with temp files, except when the given
2158 # arg is a filename
2159 use_temp = True
2160
2161 data = ''
2162
2163 # First, see if the arguments should be a filename.
2164 filename = make_filename(args)
2165 if filename:
2166 use_temp = False
2167 elif args:
2168 # Mode where user specifies ranges of lines, like in %macro.
2169 data = self.extract_input_lines(args, opts_raw)
2170 if not data:
2171 try:
2172 # Load the parameter given as a variable. If not a string,
2173 # process it as an object instead (below)
2174
2175 #print '*** args',args,'type',type(args) # dbg
2176 data = eval(args, self.shell.user_ns)
2177 if not isinstance(data, basestring):
2178 raise DataIsObject
2179
2180 except (NameError,SyntaxError):
2181 # given argument is not a variable, try as a filename
2182 filename = make_filename(args)
2183 if filename is None:
2184 warn("Argument given (%s) can't be found as a variable "
2185 "or as a filename." % args)
2186 return
2187 use_temp = False
2188
2189 except DataIsObject:
2190 # macros have a special edit function
2191 if isinstance(data, Macro):
2192 raise MacroToEdit(data)
2193
2194 # For objects, try to edit the file where they are defined
2195 try:
2196 filename = inspect.getabsfile(data)
2197 if 'fakemodule' in filename.lower() and inspect.isclass(data):
2198 # class created by %edit? Try to find source
2199 # by looking for method definitions instead, the
2200 # __module__ in those classes is FakeModule.
2201 attrs = [getattr(data, aname) for aname in dir(data)]
2202 for attr in attrs:
2203 if not inspect.ismethod(attr):
2204 continue
2205 filename = inspect.getabsfile(attr)
2206 if filename and 'fakemodule' not in filename.lower():
2207 # change the attribute to be the edit target instead
2208 data = attr
2209 break
2210
2211 datafile = 1
2212 except TypeError:
2213 filename = make_filename(args)
2214 datafile = 1
2215 warn('Could not find file where `%s` is defined.\n'
2216 'Opening a file named `%s`' % (args,filename))
2217 # Now, make sure we can actually read the source (if it was in
2218 # a temp file it's gone by now).
2219 if datafile:
2220 try:
2221 if lineno is None:
2222 lineno = inspect.getsourcelines(data)[1]
2223 except IOError:
2224 filename = make_filename(args)
2225 if filename is None:
2226 warn('The file `%s` where `%s` was defined cannot '
2227 'be read.' % (filename,data))
2228 return
2229 use_temp = False
2230
2231 if use_temp:
2232 filename = self.shell.mktempfile(data)
2233 print 'IPython will make a temporary file named:',filename
2234
2235 return filename, lineno, use_temp
2114
2236
2115 def _edit_macro(self,mname,macro):
2237 def _edit_macro(self,mname,macro):
2116 """open an editor with the macro data in a file"""
2238 """open an editor with the macro data in a file"""
@@ -2126,7 +2248,7 b' Currently the magic system has the following functions:\\n"""'
2126 def magic_ed(self,parameter_s=''):
2248 def magic_ed(self,parameter_s=''):
2127 """Alias to %edit."""
2249 """Alias to %edit."""
2128 return self.magic_edit(parameter_s)
2250 return self.magic_edit(parameter_s)
2129
2251
2130 @skip_doctest
2252 @skip_doctest
2131 def magic_edit(self,parameter_s='',last_call=['','']):
2253 def magic_edit(self,parameter_s='',last_call=['','']):
2132 """Bring up an editor and execute the resulting code.
2254 """Bring up an editor and execute the resulting code.
@@ -2269,122 +2391,13 b' Currently the magic system has the following functions:\\n"""'
2269 starting example for further modifications. That file also has
2391 starting example for further modifications. That file also has
2270 general instructions on how to set a new hook for use once you've
2392 general instructions on how to set a new hook for use once you've
2271 defined it."""
2393 defined it."""
2272
2273 # FIXME: This function has become a convoluted mess. It needs a
2274 # ground-up rewrite with clean, simple logic.
2275
2276 def make_filename(arg):
2277 "Make a filename from the given args"
2278 try:
2279 filename = get_py_filename(arg)
2280 except IOError:
2281 if args.endswith('.py'):
2282 filename = arg
2283 else:
2284 filename = None
2285 return filename
2286
2287 # custom exceptions
2288 class DataIsObject(Exception): pass
2289
2290 opts,args = self.parse_options(parameter_s,'prxn:')
2394 opts,args = self.parse_options(parameter_s,'prxn:')
2291 # Set a few locals from the options for convenience:
2292 opts_prev = 'p' in opts
2293 opts_raw = 'r' in opts
2294
2395
2295 # Default line number value
2296 lineno = opts.get('n',None)
2297
2298 if opts_prev:
2299 args = '_%s' % last_call[0]
2300 if not self.shell.user_ns.has_key(args):
2301 args = last_call[1]
2302
2303 # use last_call to remember the state of the previous call, but don't
2304 # let it be clobbered by successive '-p' calls.
2305 try:
2396 try:
2306 last_call[0] = self.shell.displayhook.prompt_count
2397 filename, lineno, is_temp = self._find_edit_target(args, opts, last_call)
2307 if not opts_prev:
2398 except MacroToEdit as e:
2308 last_call[1] = parameter_s
2399 self._edit_macro(args, e.args[0])
2309 except:
2400 return
2310 pass
2311
2312 # by default this is done with temp files, except when the given
2313 # arg is a filename
2314 use_temp = True
2315
2316 data = ''
2317 if args.endswith('.py'):
2318 filename = make_filename(args)
2319 use_temp = False
2320 elif args:
2321 # Mode where user specifies ranges of lines, like in %macro.
2322 data = self.extract_input_lines(args, opts_raw)
2323 if not data:
2324 try:
2325 # Load the parameter given as a variable. If not a string,
2326 # process it as an object instead (below)
2327
2328 #print '*** args',args,'type',type(args) # dbg
2329 data = eval(args, self.shell.user_ns)
2330 if not isinstance(data, basestring):
2331 raise DataIsObject
2332
2333 except (NameError,SyntaxError):
2334 # given argument is not a variable, try as a filename
2335 filename = make_filename(args)
2336 if filename is None:
2337 warn("Argument given (%s) can't be found as a variable "
2338 "or as a filename." % args)
2339 return
2340 use_temp = False
2341
2342 except DataIsObject:
2343 # macros have a special edit function
2344 if isinstance(data, Macro):
2345 self._edit_macro(args,data)
2346 return
2347
2348 # For objects, try to edit the file where they are defined
2349 try:
2350 filename = inspect.getabsfile(data)
2351 if 'fakemodule' in filename.lower() and inspect.isclass(data):
2352 # class created by %edit? Try to find source
2353 # by looking for method definitions instead, the
2354 # __module__ in those classes is FakeModule.
2355 attrs = [getattr(data, aname) for aname in dir(data)]
2356 for attr in attrs:
2357 if not inspect.ismethod(attr):
2358 continue
2359 filename = inspect.getabsfile(attr)
2360 if filename and 'fakemodule' not in filename.lower():
2361 # change the attribute to be the edit target instead
2362 data = attr
2363 break
2364
2365 datafile = 1
2366 except TypeError:
2367 filename = make_filename(args)
2368 datafile = 1
2369 warn('Could not find file where `%s` is defined.\n'
2370 'Opening a file named `%s`' % (args,filename))
2371 # Now, make sure we can actually read the source (if it was in
2372 # a temp file it's gone by now).
2373 if datafile:
2374 try:
2375 if lineno is None:
2376 lineno = inspect.getsourcelines(data)[1]
2377 except IOError:
2378 filename = make_filename(args)
2379 if filename is None:
2380 warn('The file `%s` where `%s` was defined cannot '
2381 'be read.' % (filename,data))
2382 return
2383 use_temp = False
2384
2385 if use_temp:
2386 filename = self.shell.mktempfile(data)
2387 print 'IPython will make a temporary file named:',filename
2388
2401
2389 # do actual editing here
2402 # do actual editing here
2390 print 'Editing...',
2403 print 'Editing...',
@@ -2392,7 +2405,7 b' Currently the magic system has the following functions:\\n"""'
2392 try:
2405 try:
2393 # Quote filenames that may have spaces in them
2406 # Quote filenames that may have spaces in them
2394 if ' ' in filename:
2407 if ' ' in filename:
2395 filename = "%s" % filename
2408 filename = "'%s'" % filename
2396 self.shell.hooks.editor(filename,lineno)
2409 self.shell.hooks.editor(filename,lineno)
2397 except TryNext:
2410 except TryNext:
2398 warn('Could not open editor')
2411 warn('Could not open editor')
@@ -2407,15 +2420,14 b' Currently the magic system has the following functions:\\n"""'
2407 print
2420 print
2408 else:
2421 else:
2409 print 'done. Executing edited code...'
2422 print 'done. Executing edited code...'
2410 if opts_raw:
2423 if 'r' in opts: # Untranslated IPython code
2411 self.shell.run_cell(file_read(filename),
2424 self.shell.run_cell(file_read(filename),
2412 store_history=False)
2425 store_history=False)
2413 else:
2426 else:
2414 self.shell.safe_execfile(filename,self.shell.user_ns,
2427 self.shell.safe_execfile(filename,self.shell.user_ns,
2415 self.shell.user_ns)
2428 self.shell.user_ns)
2416
2417
2429
2418 if use_temp:
2430 if is_temp:
2419 try:
2431 try:
2420 return open(filename).read()
2432 return open(filename).read()
2421 except IOError,msg:
2433 except IOError,msg:
@@ -29,6 +29,7 b' from IPython.core.autocall import ZMQExitAutocall'
29 from IPython.core.displayhook import DisplayHook
29 from IPython.core.displayhook import DisplayHook
30 from IPython.core.displaypub import DisplayPublisher
30 from IPython.core.displaypub import DisplayPublisher
31 from IPython.core.macro import Macro
31 from IPython.core.macro import Macro
32 from IPython.core.magic import MacroToEdit
32 from IPython.core.payloadpage import install_payload_page
33 from IPython.core.payloadpage import install_payload_page
33 from IPython.utils import io
34 from IPython.utils import io
34 from IPython.utils.path import get_py_filename
35 from IPython.utils.path import get_py_filename
@@ -399,130 +400,14 b' class ZMQInteractiveShell(InteractiveShell):'
399 general instructions on how to set a new hook for use once you've
400 general instructions on how to set a new hook for use once you've
400 defined it."""
401 defined it."""
401
402
402 # FIXME: This function has become a convoluted mess. It needs a
403 # ground-up rewrite with clean, simple logic.
404
405 def make_filename(arg):
406 "Make a filename from the given args"
407 try:
408 filename = get_py_filename(arg)
409 except IOError:
410 if args.endswith('.py'):
411 filename = arg
412 else:
413 filename = None
414 return filename
415
416 # custom exceptions
417 class DataIsObject(Exception): pass
418
419 opts,args = self.parse_options(parameter_s,'prn:')
403 opts,args = self.parse_options(parameter_s,'prn:')
420 # Set a few locals from the options for convenience:
421 opts_p = opts.has_key('p')
422 opts_r = opts.has_key('r')
423
404
424 # Default line number value
425 lineno = opts.get('n',None)
426 if lineno is not None:
427 try:
428 lineno = int(lineno)
429 except:
430 warn("The -n argument must be an integer.")
431 return
432
433 if opts_p:
434 args = '_%s' % last_call[0]
435 if not self.shell.user_ns.has_key(args):
436 args = last_call[1]
437
438 # use last_call to remember the state of the previous call, but don't
439 # let it be clobbered by successive '-p' calls.
440 try:
405 try:
441 last_call[0] = self.shell.displayhook.prompt_count
406 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
442 if not opts_p:
407 except MacroToEdit as e:
443 last_call[1] = parameter_s
408 # TODO: Implement macro editing over 2 processes.
444 except:
409 print("Macro editing not yet implemented in 2-process model.")
445 pass
410 return
446
447 # by default this is done with temp files, except when the given
448 # arg is a filename
449 use_temp = True
450
451 data = ''
452 if args[0].isdigit():
453 # Mode where user specifies ranges of lines, like in %macro.
454 # This means that you can't edit files whose names begin with
455 # numbers this way. Tough.
456 ranges = args.split()
457 data = ''.join(self.extract_input_slices(ranges,opts_r))
458 elif args.endswith('.py'):
459 filename = make_filename(args)
460 use_temp = False
461 elif args:
462 try:
463 # Load the parameter given as a variable. If not a string,
464 # process it as an object instead (below)
465
466 #print '*** args',args,'type',type(args) # dbg
467 data = eval(args, self.shell.user_ns)
468 if not isinstance(data, basestring):
469 raise DataIsObject
470
471 except (NameError,SyntaxError):
472 # given argument is not a variable, try as a filename
473 filename = make_filename(args)
474 if filename is None:
475 warn("Argument given (%s) can't be found as a variable "
476 "or as a filename." % args)
477 return
478 use_temp = False
479
480 except DataIsObject:
481 # macros have a special edit function
482 if isinstance(data, Macro):
483 self._edit_macro(args,data)
484 return
485
486 # For objects, try to edit the file where they are defined
487 try:
488 filename = inspect.getabsfile(data)
489 if 'fakemodule' in filename.lower() and inspect.isclass(data):
490 # class created by %edit? Try to find source
491 # by looking for method definitions instead, the
492 # __module__ in those classes is FakeModule.
493 attrs = [getattr(data, aname) for aname in dir(data)]
494 for attr in attrs:
495 if not inspect.ismethod(attr):
496 continue
497 filename = inspect.getabsfile(attr)
498 if filename and 'fakemodule' not in filename.lower():
499 # change the attribute to be the edit target instead
500 data = attr
501 break
502
503 datafile = 1
504 except TypeError:
505 filename = make_filename(args)
506 datafile = 1
507 warn('Could not find file where `%s` is defined.\n'
508 'Opening a file named `%s`' % (args,filename))
509 # Now, make sure we can actually read the source (if it was in
510 # a temp file it's gone by now).
511 if datafile:
512 try:
513 if lineno is None:
514 lineno = inspect.getsourcelines(data)[1]
515 except IOError:
516 filename = make_filename(args)
517 if filename is None:
518 warn('The file `%s` where `%s` was defined cannot '
519 'be read.' % (filename,data))
520 return
521 use_temp = False
522
523 if use_temp:
524 filename = self.shell.mktempfile(data)
525 print('IPython will make a temporary file named:', filename)
526
411
527 # Make sure we send to the client an absolute path, in case the working
412 # Make sure we send to the client an absolute path, in case the working
528 # directory of client and kernel don't match
413 # directory of client and kernel don't match
General Comments 0
You need to be logged in to leave comments. Login now