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