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

1"""Logger class for IPython's logging facilities. 

2""" 

3 

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#***************************************************************************** 

11 

12#**************************************************************************** 

13# Modules and globals 

14 

15# Python standard modules 

16import glob 

17import io 

18import os 

19import time 

20 

21 

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... 

25 

26class Logger(object): 

27 """A Logfile class with different policies for file creation""" 

28 

29 def __init__(self, home_dir, logfname='Logger.log', loghead=u'', 

30 logmode='over'): 

31 

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 

35 

36 self.logfname = logfname 

37 self.loghead = loghead 

38 self.logmode = logmode 

39 self.logfile = None 

40 

41 # Whether to log raw or processed input 

42 self.log_raw_input = False 

43 

44 # whether to also log output 

45 self.log_output = False 

46 

47 # whether to put timestamps before each log entry 

48 self.timestamp = False 

49 

50 # activity control flags 

51 self.log_active = False 

52 

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 

58 

59 def _get_mode(self): 

60 return self._logmode 

61 

62 logmode = property(_get_mode,_set_mode) 

63 

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. 

67 

68 Raises RuntimeError if the log has already been started""" 

69 

70 if self.logfile is not None: 

71 raise RuntimeError('Log file is already active: %s' % 

72 self.logfname) 

73 

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 

78 

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 

83 

84 # init depending on the log mode requested 

85 isfile = os.path.isfile 

86 logmode = self.logmode 

87 

88 if logmode == 'append': 

89 self.logfile = io.open(self.logfname, 'a', encoding='utf-8') 

90 

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') 

100 

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') 

104 

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') 

109 

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') 

122 

123 if logmode != 'append': 

124 self.logfile.write(self.loghead) 

125 

126 self.logfile.flush() 

127 self.log_active = True 

128 

129 def switch_log(self,val): 

130 """Switch logging on/off. val should be ONLY a boolean.""" 

131 

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) 

135 

136 label = {0:'OFF',1:'ON',False:'OFF',True:'ON'} 

137 

138 if self.logfile is None: 

139 print(""" 

140Logging hasn't been started yet (use logstart for that). 

141 

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).""") 

145 

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 

153 

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) 

166 

167 def log(self, line_mod, line_ori): 

168 """Write the sources to a log. 

169 

170 Inputs: 

171 

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. 

175 

176 - line_ori: unmodified input line from the user. This is not 

177 necessarily valid Python. 

178 """ 

179 

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) 

186 

187 def log_write(self, data, kind='input'): 

188 """Write data to the log file, if active""" 

189 

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 ) 

211 

212 def logstop(self): 

213 """Fully stop logging and close log file. 

214 

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.""" 

218 

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 

225 

226 # For backwards compatibility, in case anyone was using this. 

227 close_log = logstop