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