Presents a console permitting inspection. Input as well as output saved in Python-readable form. Python 2.5.1 memoryconsole4.py logging to My Documents\console.log >>> class A: ... def f( self ): ... print 2 ... >>> a=A() >>> import inspect >>> inspect.getsource( a.f ) '\tdef f( self ):\n\t\tprint 2\n'
This enabled by a log file, optionally set to console.log. Contents are: #Mon May 07 2007 06:33:42 PM Python win32 2.5.1 in memoryconsole4.py class A: def f( self ): print 2 a=A() import inspect inspect.getsource( a.f ) #fb: '\tdef f( self ):\n\t\tprint 2\n' Line 10 Microsoft Win32 convenience binding; line 49 a little confusing. Give it a shot. from code import InteractiveConsole import sys from os import environ from datetime import datetime from StringIO import StringIO from re import sub from os.path import join,split,abspath class LoggedStdOut(StringIO): deflog= environ['USERPROFILE']+\ '\\My Documents\\console.log' def __init__( self, log=None ): StringIO.__init__( self ) self.stdout= None self.trip,self.head= True,'' self.logname= log or LoggedStdOut.deflog self.prettyname=join(split(split(abspath( self.logname))[0])[1],split(abspath(self.logname))[1]) for x,_ in enumerate( open( self.logname,'r' ) ): continue self._lineno= x #can use linecache self._log= open( self.logname,'a' ) def catch( self,head='#fb: ' ): self.stdout= sys.stdout sys.stdout= self self.head= head self.trip= True def throw( self ): sys.stdout= self.stdout self.stdout= None def getlineno( self ): return self._lineno def logwrite( self, data ): self._log.write( data ) self._lineno+= data.count('\n') def logflush( self ): self._log.flush() def write( self, data ): datal= sub( '\n([^$])','\n%s\\1'%self.head,data ) if self.trip: self.logwrite( self.head ) self.logwrite( datal ) self.trip= data.endswith('\n') return self.stdout.write( data ) def writelines( self, data ): raise 'Branch uncoded' class LoggedInteractiveConsole(InteractiveConsole): def __init__( self,log=None,locals=None,filename=None ): self.out= LoggedStdOut( log ) if filename is None: filename= split(self.out.logname)[1] InteractiveConsole.__init__( self,locals,filename ) self.locals.update( __logname__= abspath( self.out.logname ) ) def push( self,line ): self.out.logwrite( '%s\n'%line ) self.out.logflush() self.out.catch() more= InteractiveConsole.push( self,line ) self.out.throw() return more def write( self,data ): return sys.stdout.write( data ) def interact( self,banner=None,*args ): self.out.logwrite( '\n#%s Python %s %s in %s\n'%\ ( datetime.now().strftime( '%a %b %d %Y %I:%M:%S %p' ), sys.platform,sys.version.split()[0], split(sys.argv[0])[1] ) ) if banner is None: banner=\ "Python %s %s logging to %s"%\ ( sys.version.split()[0],split(sys.argv[0])[1], self.out.prettyname ) return InteractiveConsole.interact( self,banner,*args ) import compiler import linecache class NotatedConsole(LoggedInteractiveConsole): """-Code object- intercepted in runsource, and rerun with stored source before runsource. Built-in runsource does not modify source between call and runcode.""" def runsource( self,sc,filename='<input>',*args ): self._runsourceargs= sc,filename return LoggedInteractiveConsole.runsource( self,sc, filename,*args ) def runcode( self,*args ): sc,filename= self._runsourceargs linecache.checkcache( filename ) #custom second compile (fourth actually) t= compiler.parse( sc ) compiler.misc.set_filename( filename,t ) def set_lineno( tree, initlineno ): worklist= [ tree ] while worklist: node= worklist.pop( 0 ) if node.lineno is not None: node.lineno+= initlineno worklist.extend( node.getChildNodes() ) set_lineno( t,self.out.getlineno()-len( self.buffer )+1 ) code= compiler.pycodegen.\ InteractiveCodeGenerator( t ).getCode() LoggedInteractiveConsole.runcode( self,code ) linecache.checkcache( filename ) if __name__=='__main__': console= NotatedConsole() console.interact() -- http://mail.python.org/mailman/listinfo/python-list