##// END OF EJS Templates
editor synchronization
editor synchronization

File last commit:

r896:863dd69f
r1243:92aa0c13
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