##// END OF EJS Templates
New %pycat magic.
New %pycat magic.

File last commit:

r0:6f629fcc
r29:b969ddd9
Show More
Logger.py
185 lines | 6.9 KiB | text/x-python | PythonLexer
# -*- coding: utf-8 -*-
"""
Logger class for IPython's logging facilities.
$Id: Logger.py 430 2004-11-30 08:52:05Z fperez $
"""
#*****************************************************************************
# Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
# Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
#
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
#*****************************************************************************
#****************************************************************************
# Modules and globals
from IPython import Release
__author__ = '%s <%s>\n%s <%s>' % \
( Release.authors['Janko'] + Release.authors['Fernando'] )
__license__ = Release.license
# Python standard modules
import os,sys,glob
# Homebrewed
from IPython.genutils import *
#****************************************************************************
# FIXME: The logger class shouldn't be a mixin, it throws too many things into
# the InteractiveShell namespace. Rather make it a standalone tool, and create
# a Logger instance in InteractiveShell that uses it. Doing this will require
# tracking down a *lot* of nasty uses of the Logger attributes in
# InteractiveShell, but will clean up things quite a bit.
class Logger:
"""A Logfile Mixin class with different policies for file creation"""
# FIXME: once this isn't a mixin, log_ns should just be 'namespace', since the
# names won't collide anymore.
def __init__(self,log_ns):
self._i00,self._i,self._ii,self._iii = '','','',''
self.do_full_cache = 0 # FIXME. There's also a do_full.. in OutputCache
self.log_ns = log_ns
# defaults
self.LOGMODE = 'backup'
self.defname = 'logfile'
def create_log(self,header='',fname='',defname='.Logger.log'):
"""Generate a new log-file with a default header"""
if fname:
self.LOG = fname
if self.LOG:
self.logfname = self.LOG
else:
self.logfname = defname
if self.LOGMODE == 'over':
if os.path.isfile(self.logfname):
os.remove(self.logfname)
self.logfile = open(self.logfname,'w')
if self.LOGMODE == 'backup':
if os.path.isfile(self.logfname):
backup_logname = self.logfname+'~'
# Manually remove any old backup, since os.rename may fail
# under Windows.
if os.path.isfile(backup_logname):
os.remove(backup_logname)
os.rename(self.logfname,backup_logname)
self.logfile = open(self.logfname,'w')
elif self.LOGMODE == 'global':
self.logfname = os.path.join(self.home_dir, self.defname)
self.logfile = open(self.logfname, 'a')
self.LOG = self.logfname
elif self.LOGMODE == 'rotate':
if os.path.isfile(self.logfname):
if os.path.isfile(self.logfname+'.001~'):
old = glob.glob(self.logfname+'.*~')
old.sort()
old.reverse()
for f in old:
root, ext = os.path.splitext(f)
num = int(ext[1:-1])+1
os.rename(f, root+'.'+`num`.zfill(3)+'~')
os.rename(self.logfname, self.logfname+'.001~')
self.logfile = open(self.logfname,'w')
elif self.LOGMODE == 'append':
self.logfile = open(self.logfname,'a')
if self.LOGMODE != 'append':
self.logfile.write(header)
self.logfile.flush()
def logstart(self, header='',parameter_s = ''):
if not hasattr(self, 'LOG'):
logfname = self.LOG or parameter_s or './'+self.defname
self.create_log(header,logfname)
elif parameter_s and hasattr(self,'logfname') and \
parameter_s != self.logfname:
self.close_log()
self.create_log(header,parameter_s)
self._dolog = 1
def switch_log(self,val):
"""Switch logging on/off. val should be ONLY 0 or 1."""
if not val in [0,1]:
raise ValueError, \
'Call switch_log ONLY with 0 or 1 as argument, not with:',val
label = {0:'OFF',1:'ON'}
try:
_ = self.logfile
except AttributeError:
print """
Logging hasn't been started yet (use %logstart for that).
%logon/%logoff are for temporarily starting and stopping logging for a logfile
which already exists. But you must first start the logging process with
%logstart (optionally giving a logfile name)."""
else:
if self._dolog == val:
print 'Logging is already',label[val]
else:
print 'Switching logging',label[val]
self._dolog = 1 - self._dolog
def logstate(self):
"""Print a status message about the logger."""
try:
logfile = self.logfname
except:
print 'Logging has not been activated.'
else:
state = self._dolog and 'active' or 'temporarily suspended'
print """
File:\t%s
Mode:\t%s
State:\t%s """ % (logfile,self.LOGMODE,state)
def log(self, line,continuation=None):
"""Write the line to a log and create input cache variables _i*."""
# update the auto _i tables
#print '***logging line',line # dbg
#print '***cache_count', self.outputcache.prompt_count # dbg
input_hist = self.log_ns['_ih']
if not continuation and line:
self._iii = self._ii
self._ii = self._i
self._i = self._i00
# put back the final \n of every input line
self._i00 = line+'\n'
#print 'Logging input:<%s>' % line # dbg
input_hist.append(self._i00)
# hackish access to top-level namespace to create _i1,_i2... dynamically
to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
if self.do_full_cache:
in_num = self.outputcache.prompt_count
# add blank lines if the input cache fell out of sync. This can happen
# for embedded instances which get killed via C-D and then get resumed.
while in_num >= len(input_hist):
input_hist.append('\n')
new_i = '_i%s' % in_num
if continuation:
self._i00 = '%s%s\n' % (self.log_ns[new_i],line)
input_hist[in_num] = self._i00
to_main[new_i] = self._i00
self.log_ns.update(to_main)
if self._dolog and line:
self.logfile.write(line+'\n')
self.logfile.flush()
def close_log(self):
if hasattr(self, 'logfile'):
self.logfile.close()
self.logfname = ''