From 942f15ecd19229c8454b9f95da7e36325712d742 2005-12-29 23:50:22 From: fperez Date: 2005-12-29 23:50:22 Subject: [PATCH] - thread-safety fixes - moved macros to their own module (stabilize the API for %store) - fix handling of empty lines in multiline input which comes via runlines() and not interactively (such as macros or over the network protocol in the chainsaw branch) - improvements to %whos for instance variables --- diff --git a/IPython/Magic.py b/IPython/Magic.py index baada61..14cd5ce 100644 --- a/IPython/Magic.py +++ b/IPython/Magic.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Magic functions for InteractiveShell. -$Id: Magic.py 974 2005-12-29 19:48:33Z fperez $""" +$Id: Magic.py 975 2005-12-29 23:50:22Z fperez $""" #***************************************************************************** # Copyright (C) 2001 Janko Hauser and @@ -47,6 +47,7 @@ from IPython.FakeModule import FakeModule from IPython.Itpl import Itpl, itpl, printpl,itplns from IPython.PyColorize import Parser from IPython.Struct import Struct +from IPython.macro import Macro from IPython.genutils import * #*************************************************************************** @@ -56,18 +57,6 @@ def on_off(tag): return ['OFF','ON'][tag] -#**************************************************************************** -# Utility classes -class Macro(list): - """Simple class to store the value of macros as strings. - - This allows us to later exec them by checking when something is an - instance of this class.""" - - def __init__(self,data): - list.__init__(self,data) - self.value = ''.join(data) - #*************************************************************************** # Main class implementing Magic functionality class Magic: @@ -224,13 +213,12 @@ license. To use profiling, please install"python2.3-profiler" from non-free.""") return {'found':found, 'obj':obj, 'namespace':ospace, 'ismagic':ismagic, 'isalias':isalias} - + def arg_err(self,func): """Print docstring if incorrect arguments were passed""" print 'Error in arguments:' print OInspect.getdoc(func) - def format_latex(self,strng): """Format a string for latex inclusion.""" @@ -845,7 +833,15 @@ Currently the magic system has the following functions:\n""" get_vars = lambda i: self.shell.user_ns[i] type_name = lambda v: type(v).__name__ varlist = map(get_vars,varnames) - typelist = map(type_name,varlist) + + typelist = [] + for vv in varlist: + tt = type_name(vv) + if tt=='instance': + typelist.append(str(vv.__class__)) + else: + typelist.append(tt) + # column labels and # of spaces as separator varlabel = 'Variable' typelabel = 'Type' @@ -881,7 +877,7 @@ Currently the magic system has the following functions:\n""" else: print '(%s Mb)' % (vbytes/Mb,) else: - vstr = str(var) + vstr = str(var).replace('\n','\\n') if len(vstr) < 50: print vstr else: @@ -1593,7 +1589,7 @@ Currently the magic system has the following functions:\n""" self.shell.user_ns.update({name:macro}) print 'Macro `%s` created. To execute, type its name (without quotes).' % name print 'Macro contents:' - print macro + print macro, def magic_save(self,parameter_s = ''): """Save a set of lines to a given filename. diff --git a/IPython/Prompts.py b/IPython/Prompts.py index 72b7777..07be7e8 100644 --- a/IPython/Prompts.py +++ b/IPython/Prompts.py @@ -2,7 +2,7 @@ """ Classes for handling input/output prompts. -$Id: Prompts.py 966 2005-12-29 08:34:07Z fperez $""" +$Id: Prompts.py 975 2005-12-29 23:50:22Z fperez $""" #***************************************************************************** # Copyright (C) 2001-2004 Fernando Perez @@ -26,11 +26,11 @@ import time from pprint import pprint,pformat # IPython's own -from IPython.genutils import * -from IPython.Struct import Struct -from IPython.Magic import Macro -from IPython.Itpl import ItplNS from IPython import ColorANSI +from IPython.Itpl import ItplNS +from IPython.Struct import Struct +from IPython.macro import Macro +from IPython.genutils import * #**************************************************************************** #Color schemes for Prompts. diff --git a/IPython/iplib.py b/IPython/iplib.py index 1ceccb7..ad717d2 100644 --- a/IPython/iplib.py +++ b/IPython/iplib.py @@ -6,7 +6,7 @@ Requires Python 2.1 or newer. This file contains all the classes and helper functions specific to IPython. -$Id: iplib.py 968 2005-12-29 17:15:38Z fperez $ +$Id: iplib.py 975 2005-12-29 23:50:22Z fperez $ """ #***************************************************************************** @@ -78,6 +78,10 @@ from IPython.genutils import * # overwrites it (like wx.py.PyShell does) raw_input_original = raw_input +# compiled regexps for autoindent management +ini_spaces_re = re.compile(r'^(\s+)') +dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass') + #**************************************************************************** # Some utility function definitions @@ -1171,10 +1175,8 @@ want to merge them back into the new files.""" % locals() of what was there before (because Python's parser always uses "" when reading from a string). """ - type, value, sys.last_traceback = sys.exc_info() - sys.last_type = type - sys.last_value = value - if filename and type is SyntaxError: + etype, value, last_traceback = sys.exc_info() + if filename and etype is SyntaxError: # Work hard to stuff the correct filename in the exception try: msg, (dummy_filename, lineno, offset, line) = value @@ -1189,7 +1191,7 @@ want to merge them back into the new files.""" % locals() except: # If that failed, assume SyntaxError is a string value = msg, (filename, lineno, offset, line) - self.SyntaxTB(type,value,[]) + self.SyntaxTB(etype,value,[]) def debugger(self): """Call the pdb debugger.""" @@ -1210,9 +1212,6 @@ want to merge them back into the new files.""" % locals() if type is SyntaxError: self.showsyntaxerror(filename) else: - sys.last_type = type - sys.last_value = value - sys.last_traceback = tb self.InteractiveTB() if self.InteractiveTB.call_pdb and self.has_readline: # pdb mucks up readline, fix it back @@ -1220,7 +1219,10 @@ want to merge them back into the new files.""" % locals() def update_cache(self, line): """puts line into cache""" - self.inputcache.insert(0, line) # This copies the cache every time ... :-( + return # dbg + + # This copies the cache every time ... :-( + self.inputcache.insert(0, line) if len(self.inputcache) >= self.CACHELENGTH: self.inputcache.pop() # This doesn't :-) @@ -1319,10 +1321,6 @@ want to merge them back into the new files.""" % locals() # Mark activity in the builtins __builtin__.__dict__['__IPYTHON__active'] += 1 - # compiled regexps for autoindent management - ini_spaces_re = re.compile(r'^(\s+)') - dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass') - # exit_now is set by a call to %Exit or %Quit while not self.exit_now: try: @@ -1343,26 +1341,6 @@ want to merge them back into the new files.""" % locals() self.exit() else: more = self.push(line) - # Auto-indent management - if self.autoindent: - if line: - ini_spaces = ini_spaces_re.match(line) - if ini_spaces: - nspaces = ini_spaces.end() - else: - nspaces = 0 - self.indent_current_nsp = nspaces - - if line[-1] == ':': - self.indent_current_nsp += 4 - elif dedent_re.match(line): - self.indent_current_nsp -= 4 - else: - self.indent_current_nsp = 0 - - # indent_current is the actual string to be inserted - # by the readline hooks for indentation - self.indent_current = ' '* self.indent_current_nsp if (self.SyntaxTB.last_syntax_error and self.rc.autoedit_syntax): @@ -1445,6 +1423,28 @@ want to merge them back into the new files.""" % locals() except: self.showtraceback() + def autoindent_update(self,line): + """Keep track of the indent level.""" + if self.autoindent: + if line: + ini_spaces = ini_spaces_re.match(line) + if ini_spaces: + nspaces = ini_spaces.end() + else: + nspaces = 0 + self.indent_current_nsp = nspaces + + if line[-1] == ':': + self.indent_current_nsp += 4 + elif dedent_re.match(line): + self.indent_current_nsp -= 4 + else: + self.indent_current_nsp = 0 + + # indent_current is the actual string to be inserted + # by the readline hooks for indentation + self.indent_current = ' '* self.indent_current_nsp + def runlines(self,lines): """Run a string of one or more lines of source. @@ -1462,8 +1462,11 @@ want to merge them back into the new files.""" % locals() # skip blank lines so we don't mess up the prompt counter, but do # NOT skip even a blank line if we are in a code block (more is # true) + #print 'rl line:<%s>' % line # dbg if line or more: - more = self.push((self.prefilter(line,more))) + #print 'doit' # dbg + newline = self.prefilter(line,more) + more = self.push(newline) # IPython's runsource returns None if there was an error # compiling the code. This allows us to stop processing right # away, so the user gets the error message at the right place. @@ -1716,6 +1719,8 @@ want to merge them back into the new files.""" % locals() # Let's try to find if the input line is a magic fn oinfo = None if hasattr(self,'magic_'+iFun): + # WARNING: _ofind uses getattr(), so it can consume generators and + # cause other side effects. oinfo = self._ofind(iFun) # FIXME - _ofind is part of Magic if oinfo['ismagic']: # Be careful not to call magics when a variable assignment is diff --git a/IPython/macro.py b/IPython/macro.py new file mode 100644 index 0000000..9349460 --- /dev/null +++ b/IPython/macro.py @@ -0,0 +1,23 @@ +"""Support for interactive macros in IPython""" + +#***************************************************************************** +# Copyright (C) 2001-2005 Fernando Perez +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +class Macro: + """Simple class to store the value of macros as strings. + + This allows us to later exec them by checking when something is an + instance of this class.""" + + def __init__(self,data): + + # store the macro value, as a single string which can be evaluated by + # runlines() + self.value = ''.join(data).rstrip()+'\n' + + def __str__(self): + return self.value diff --git a/IPython/ultraTB.py b/IPython/ultraTB.py index 74db9f4..7820af1 100644 --- a/IPython/ultraTB.py +++ b/IPython/ultraTB.py @@ -60,7 +60,7 @@ You can implement other color schemes easily, the syntax is fairly self-explanatory. Please send back new schemes you develop to the author for possible inclusion in future releases. -$Id: ultraTB.py 965 2005-12-28 23:23:09Z fperez $""" +$Id: ultraTB.py 975 2005-12-29 23:50:22Z fperez $""" #***************************************************************************** # Copyright (C) 2001 Nathaniel Gray @@ -225,12 +225,12 @@ class ListTB(TBTools): """Format the exception part of a traceback. The arguments are the exception type and value such as given by - sys.last_type and sys.last_value. The return value is a list of - strings, each ending in a newline. Normally, the list contains a - single string; however, for SyntaxError exceptions, it contains - several lines that (when printed) display detailed information - about where the syntax error occurred. The message indicating - which exception occurred is the always last string in the list. + sys.exc_info()[:2]. The return value is a list of strings, each ending + in a newline. Normally, the list contains a single string; however, + for SyntaxError exceptions, it contains several lines that (when + printed) display detailed information about where the syntax error + occurred. The message indicating which exception occurred is the + always last string in the list. Also lifted nearly verbatim from traceback.py """ diff --git a/doc/ChangeLog b/doc/ChangeLog index 531aad3..8eab3a8 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,8 +1,26 @@ 2005-12-29 Fernando Perez + * IPython/iplib.py (showtraceback): remove use of the + sys.last_{type/value/traceback} structures, which are non + thread-safe. + + * IPython/macro.py (Macro.__init__): moved macros to a standalone + file. Now that they'll be more likely to be used with the + persistance system (%store), I want to make sure their module path + doesn't change in the future, so that we don't break things for + users' persisted data. + + * IPython/iplib.py (autoindent_update): move indentation + management into the _text_ processing loop, not the keyboard + interactive one. This is necessary to correctly process non-typed + multiline input (such as macros). + * IPython/Magic.py (Magic.format_latex): patch by Stefan van der Walt to fix latex formatting of docstrings, which was producing problems in the resulting manual. + (magic_whos): improve reporting of instances (show their class, + instead of simply printing 'instance' which isn't terribly + informative). * IPython/genutils.py (shell): commit Jorgen Stenarson's patch (minor mods) to support network shares under win32.