##// END OF EJS Templates
- New dtutils module for running doctests interactively with more...
- New dtutils module for running doctests interactively with more convenience. Not fully fleshed yet, but already useful. Needs better support for specifying the package where doctests should be run.

File last commit:

r896:863dd69f
r909:932ee940
Show More
Logger.py
270 lines | 9.8 KiB | text/x-python | PythonLexer
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 # -*- coding: utf-8 -*-
"""
Logger class for IPython's logging facilities.
fperez
Fix small bug in %logstop
r896 $Id: Logger.py 2875 2007-11-26 08:37:39Z fperez $
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 """
#*****************************************************************************
# Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
fperez
Small fix in ultraTB, and fix autocall....
r88 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 #
# 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
fperez
Cosmetic cleanups: put all imports in a single line, and sort them...
r52 import glob
import os
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 import time
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
#****************************************************************************
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 # FIXME: This class isn't a mixin anymore, but it still needs attributes from
# ipython and does input cache management. Finish cleanup later...
class Logger(object):
"""A Logfile class with different policies for file creation"""
def __init__(self,shell,logfname='Logger.log',loghead='',logmode='over'):
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 self._i00,self._i,self._ii,self._iii = '','','',''
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 # this is the full ipython instance, we need some attributes from it
# which won't exist until later. What a mess, clean up later...
self.shell = shell
self.logfname = logfname
self.loghead = loghead
self.logmode = logmode
self.logfile = None
fperez
Add -r option to %logstart, to log 'raw' input instead of the processed one....
r305 # Whether to log raw or processed input
self.log_raw_input = False
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 # whether to also log output
self.log_output = False
# whether to put timestamps before each log entry
self.timestamp = False
# activity control flags
self.log_active = False
# logmode is a validated property
def _set_mode(self,mode):
if mode not in ['append','backup','global','over','rotate']:
raise ValueError,'invalid log mode %s given' % mode
self._logmode = mode
def _get_mode(self):
return self._logmode
logmode = property(_get_mode,_set_mode)
def logstart(self,logfname=None,loghead=None,logmode=None,
fperez
Add -r option to %logstart, to log 'raw' input instead of the processed one....
r305 log_output=False,timestamp=False,log_raw_input=False):
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 """Generate a new log-file with a default header.
Raises RuntimeError if the log has already been started"""
if self.logfile is not None:
raise RuntimeError('Log file is already active: %s' %
self.logfname)
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 self.log_active = True
fperez
Add -r option to %logstart, to log 'raw' input instead of the processed one....
r305 # The parameters can override constructor defaults
if logfname is not None: self.logfname = logfname
if loghead is not None: self.loghead = loghead
if logmode is not None: self.logmode = logmode
# Parameters not part of the constructor
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 self.timestamp = timestamp
self.log_output = log_output
fperez
Add -r option to %logstart, to log 'raw' input instead of the processed one....
r305 self.log_raw_input = log_raw_input
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60
# init depending on the log mode requested
isfile = os.path.isfile
logmode = self.logmode
if logmode == 'append':
self.logfile = open(self.logfname,'a')
elif logmode == 'backup':
if isfile(self.logfname):
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 backup_logname = self.logfname+'~'
# Manually remove any old backup, since os.rename may fail
# under Windows.
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 if isfile(backup_logname):
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 os.remove(backup_logname)
os.rename(self.logfname,backup_logname)
self.logfile = open(self.logfname,'w')
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60
elif logmode == 'global':
self.logfname = os.path.join(self.shell.home_dir,self.logfname)
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 self.logfile = open(self.logfname, 'a')
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60
elif logmode == 'over':
if isfile(self.logfname):
os.remove(self.logfname)
self.logfile = open(self.logfname,'w')
elif logmode == 'rotate':
if isfile(self.logfname):
if isfile(self.logfname+'.001~'):
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 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')
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 if logmode != 'append':
self.logfile.write(self.loghead)
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 self.logfile.flush()
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
def switch_log(self,val):
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 """Switch logging on/off. val should be ONLY a boolean."""
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 if val not in [False,True,0,1]:
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 raise ValueError, \
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 'Call switch_log ONLY with a boolean argument, not with:',val
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 label = {0:'OFF',1:'ON',False:'OFF',True:'ON'}
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 if self.logfile is None:
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 print """
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 Logging hasn't been started yet (use logstart for that).
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
%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:
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 if self.log_active == val:
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 print 'Logging is already',label[val]
else:
print 'Switching logging',label[val]
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 self.log_active = not self.log_active
self.log_active_out = self.log_active
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
def logstate(self):
"""Print a status message about the logger."""
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 if self.logfile is None:
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 print 'Logging has not been activated.'
else:
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 state = self.log_active and 'active' or 'temporarily suspended'
fperez
doc fixes to logging
r68 print 'Filename :',self.logfname
print 'Mode :',self.logmode
print 'Output logging :',self.log_output
fperez
Minor fix to diagnostics, forgot to list raw input.
r306 print 'Raw input log :',self.log_raw_input
fperez
doc fixes to logging
r68 print 'Timestamping :',self.timestamp
print 'State :',state
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
fperez
Add -r option to %logstart, to log 'raw' input instead of the processed one....
r305 def log(self,line_ori,line_mod,continuation=None):
"""Write the line to a log and create input cache variables _i*.
Inputs:
- line_ori: unmodified input line from the user. This is not
necessarily valid Python.
- line_mod: possibly modified input, such as the transformations made
by input prefilters or input handlers of various kinds. This should
always be valid Python.
- continuation: if True, indicates this is part of multi-line input."""
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
# update the auto _i tables
fperez
Add -r option to %logstart, to log 'raw' input instead of the processed one....
r305 #print '***logging line',line_mod # dbg
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 #print '***cache_count', self.shell.outputcache.prompt_count # dbg
fperez
Fixes to:...
r82 try:
input_hist = self.shell.user_ns['_ih']
except:
print 'userns:',self.shell.user_ns.keys()
return
vivainio
move empty line padding to before append of current line
r788 out_cache = self.shell.outputcache
# add blank lines if the input cache fell out of sync.
if out_cache.do_full_cache and \
out_cache.prompt_count +1 > len(input_hist):
input_hist.extend(['\n'] * (out_cache.prompt_count - len(input_hist)))
fperez
Add -r option to %logstart, to log 'raw' input instead of the processed one....
r305 if not continuation and line_mod:
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 self._iii = self._ii
self._ii = self._i
self._i = self._i00
# put back the final \n of every input line
fperez
Add -r option to %logstart, to log 'raw' input instead of the processed one....
r305 self._i00 = line_mod+'\n'
#print 'Logging input:<%s>' % line_mod # dbg
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 input_hist.append(self._i00)
fperez
Pdb calling, pickle (under certain circumstances, connected with %run) and...
r78 #print '---[%s]' % (len(input_hist)-1,) # dbg
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0
# hackish access to top-level namespace to create _i1,_i2... dynamically
to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 if self.shell.outputcache.do_full_cache:
in_num = self.shell.outputcache.prompt_count
vivainio
move empty line padding to before append of current line
r788
fperez
Pdb calling, pickle (under certain circumstances, connected with %run) and...
r78 # but if the opposite is true (a macro can produce multiple inputs
# with no output display called), then bring the output counter in
# sync:
last_num = len(input_hist)-1
if in_num != last_num:
in_num = self.shell.outputcache.prompt_count = last_num
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 new_i = '_i%s' % in_num
if continuation:
fperez
Add -r option to %logstart, to log 'raw' input instead of the processed one....
r305 self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod)
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 input_hist[in_num] = self._i00
to_main[new_i] = self._i00
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 self.shell.user_ns.update(to_main)
fperez
Add -r option to %logstart, to log 'raw' input instead of the processed one....
r305
# Write the log line, but decide which one according to the
# log_raw_input flag, set when the log is started.
if self.log_raw_input:
self.log_write(line_ori)
else:
self.log_write(line_mod)
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60
def log_write(self,data,kind='input'):
"""Write data to the log file, if active"""
vivainio
Merged 1071-1076 from banches/0.7.1
r145 #print 'data: %r' % data # dbg
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 if self.log_active and data:
write = self.logfile.write
if kind=='input':
if self.timestamp:
write(time.strftime('# %a, %d %b %Y %H:%M:%S\n',
time.localtime()))
write('%s\n' % data)
elif kind=='output' and self.log_output:
odata = '\n'.join(['#[Out]# %s' % s
for s in data.split('\n')])
write('%s\n' % odata)
fperez
Reorganized the directory for ipython/ to have its own dir, which is a bit...
r0 self.logfile.flush()
fperez
- Add %logstop functionality to fully stop logger after turning it on....
r895 def logstop(self):
"""Fully stop logging and close log file.
In order to start logging again, a new logstart() call needs to be
made, possibly (though not necessarily) with a new filename, mode and
other options."""
fperez
Major cleanups and changes, see changelog/changeset for full details.
r60 self.logfile.close()
self.logfile = None
fperez
- Add %logstop functionality to fully stop logger after turning it on....
r895 self.log_active = False
# For backwards compatibility, in case anyone was using this.
close_log = logstop