Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/IPython/core/logger.py: 14%
113 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
1"""Logger class for IPython's logging facilities.
2"""
4#*****************************************************************************
5# Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
6# Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
7#
8# Distributed under the terms of the BSD License. The full license is in
9# the file COPYING, distributed as part of this software.
10#*****************************************************************************
12#****************************************************************************
13# Modules and globals
15# Python standard modules
16import glob
17import io
18import os
19import time
22#****************************************************************************
23# FIXME: This class isn't a mixin anymore, but it still needs attributes from
24# ipython and does input cache management. Finish cleanup later...
26class Logger(object):
27 """A Logfile class with different policies for file creation"""
29 def __init__(self, home_dir, logfname='Logger.log', loghead=u'',
30 logmode='over'):
32 # this is the full ipython instance, we need some attributes from it
33 # which won't exist until later. What a mess, clean up later...
34 self.home_dir = home_dir
36 self.logfname = logfname
37 self.loghead = loghead
38 self.logmode = logmode
39 self.logfile = None
41 # Whether to log raw or processed input
42 self.log_raw_input = False
44 # whether to also log output
45 self.log_output = False
47 # whether to put timestamps before each log entry
48 self.timestamp = False
50 # activity control flags
51 self.log_active = False
53 # logmode is a validated property
54 def _set_mode(self,mode):
55 if mode not in ['append','backup','global','over','rotate']:
56 raise ValueError('invalid log mode %s given' % mode)
57 self._logmode = mode
59 def _get_mode(self):
60 return self._logmode
62 logmode = property(_get_mode,_set_mode)
64 def logstart(self, logfname=None, loghead=None, logmode=None,
65 log_output=False, timestamp=False, log_raw_input=False):
66 """Generate a new log-file with a default header.
68 Raises RuntimeError if the log has already been started"""
70 if self.logfile is not None:
71 raise RuntimeError('Log file is already active: %s' %
72 self.logfname)
74 # The parameters can override constructor defaults
75 if logfname is not None: self.logfname = logfname
76 if loghead is not None: self.loghead = loghead
77 if logmode is not None: self.logmode = logmode
79 # Parameters not part of the constructor
80 self.timestamp = timestamp
81 self.log_output = log_output
82 self.log_raw_input = log_raw_input
84 # init depending on the log mode requested
85 isfile = os.path.isfile
86 logmode = self.logmode
88 if logmode == 'append':
89 self.logfile = io.open(self.logfname, 'a', encoding='utf-8')
91 elif logmode == 'backup':
92 if isfile(self.logfname):
93 backup_logname = self.logfname+'~'
94 # Manually remove any old backup, since os.rename may fail
95 # under Windows.
96 if isfile(backup_logname):
97 os.remove(backup_logname)
98 os.rename(self.logfname,backup_logname)
99 self.logfile = io.open(self.logfname, 'w', encoding='utf-8')
101 elif logmode == 'global':
102 self.logfname = os.path.join(self.home_dir,self.logfname)
103 self.logfile = io.open(self.logfname, 'a', encoding='utf-8')
105 elif logmode == 'over':
106 if isfile(self.logfname):
107 os.remove(self.logfname)
108 self.logfile = io.open(self.logfname,'w', encoding='utf-8')
110 elif logmode == 'rotate':
111 if isfile(self.logfname):
112 if isfile(self.logfname+'.001~'):
113 old = glob.glob(self.logfname+'.*~')
114 old.sort()
115 old.reverse()
116 for f in old:
117 root, ext = os.path.splitext(f)
118 num = int(ext[1:-1])+1
119 os.rename(f, root+'.'+repr(num).zfill(3)+'~')
120 os.rename(self.logfname, self.logfname+'.001~')
121 self.logfile = io.open(self.logfname, 'w', encoding='utf-8')
123 if logmode != 'append':
124 self.logfile.write(self.loghead)
126 self.logfile.flush()
127 self.log_active = True
129 def switch_log(self,val):
130 """Switch logging on/off. val should be ONLY a boolean."""
132 if val not in [False,True,0,1]:
133 raise ValueError('Call switch_log ONLY with a boolean argument, '
134 'not with: %s' % val)
136 label = {0:'OFF',1:'ON',False:'OFF',True:'ON'}
138 if self.logfile is None:
139 print("""
140Logging hasn't been started yet (use logstart for that).
142%logon/%logoff are for temporarily starting and stopping logging for a logfile
143which already exists. But you must first start the logging process with
144%logstart (optionally giving a logfile name).""")
146 else:
147 if self.log_active == val:
148 print('Logging is already',label[val])
149 else:
150 print('Switching logging',label[val])
151 self.log_active = not self.log_active
152 self.log_active_out = self.log_active
154 def logstate(self):
155 """Print a status message about the logger."""
156 if self.logfile is None:
157 print('Logging has not been activated.')
158 else:
159 state = self.log_active and 'active' or 'temporarily suspended'
160 print('Filename :', self.logfname)
161 print('Mode :', self.logmode)
162 print('Output logging :', self.log_output)
163 print('Raw input log :', self.log_raw_input)
164 print('Timestamping :', self.timestamp)
165 print('State :', state)
167 def log(self, line_mod, line_ori):
168 """Write the sources to a log.
170 Inputs:
172 - line_mod: possibly modified input, such as the transformations made
173 by input prefilters or input handlers of various kinds. This should
174 always be valid Python.
176 - line_ori: unmodified input line from the user. This is not
177 necessarily valid Python.
178 """
180 # Write the log line, but decide which one according to the
181 # log_raw_input flag, set when the log is started.
182 if self.log_raw_input:
183 self.log_write(line_ori)
184 else:
185 self.log_write(line_mod)
187 def log_write(self, data, kind='input'):
188 """Write data to the log file, if active"""
190 #print 'data: %r' % data # dbg
191 if self.log_active and data:
192 write = self.logfile.write
193 if kind=='input':
194 if self.timestamp:
195 write(time.strftime('# %a, %d %b %Y %H:%M:%S\n', time.localtime()))
196 write(data)
197 elif kind=='output' and self.log_output:
198 odata = u'\n'.join([u'#[Out]# %s' % s
199 for s in data.splitlines()])
200 write(u'%s\n' % odata)
201 try:
202 self.logfile.flush()
203 except OSError:
204 print("Failed to flush the log file.")
205 print(
206 f"Please check that {self.logfname} exists and have the right permissions."
207 )
208 print(
209 "Also consider turning off the log with `%logstop` to avoid this warning."
210 )
212 def logstop(self):
213 """Fully stop logging and close log file.
215 In order to start logging again, a new logstart() call needs to be
216 made, possibly (though not necessarily) with a new filename, mode and
217 other options."""
219 if self.logfile is not None:
220 self.logfile.close()
221 self.logfile = None
222 else:
223 print("Logging hadn't been started.")
224 self.log_active = False
226 # For backwards compatibility, in case anyone was using this.
227 close_log = logstop