##// END OF EJS Templates
Fixes to utils.io.Tee
Thomas Kluyver -
Show More
@@ -1,321 +1,321 b''
1 1 # encoding: utf-8
2 2 """
3 3 IO related utilities.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2009 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12 from __future__ import print_function
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 import sys
18 18 import tempfile
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Code
22 22 #-----------------------------------------------------------------------------
23 23
24 24
25 25 class IOStream:
26 26
27 27 def __init__(self,stream, fallback=None):
28 28 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
29 29 if fallback is not None:
30 30 stream = fallback
31 31 else:
32 32 raise ValueError("fallback required, but not specified")
33 33 self.stream = stream
34 34 self._swrite = stream.write
35 35
36 36 # clone all methods not overridden:
37 37 def clone(meth):
38 38 return not hasattr(self, meth) and not meth.startswith('_')
39 39 for meth in filter(clone, dir(stream)):
40 40 setattr(self, meth, getattr(stream, meth))
41 41
42 42 def write(self,data):
43 43 try:
44 44 self._swrite(data)
45 45 except:
46 46 try:
47 47 # print handles some unicode issues which may trip a plain
48 48 # write() call. Emulate write() by using an empty end
49 49 # argument.
50 50 print(data, end='', file=self.stream)
51 51 except:
52 52 # if we get here, something is seriously broken.
53 53 print('ERROR - failed to write data to stream:', self.stream,
54 54 file=sys.stderr)
55 55
56 56 def writelines(self, lines):
57 57 if isinstance(lines, basestring):
58 58 lines = [lines]
59 59 for line in lines:
60 60 self.write(line)
61 61
62 62 # This class used to have a writeln method, but regular files and streams
63 63 # in Python don't have this method. We need to keep this completely
64 64 # compatible so we removed it.
65 65
66 66 @property
67 67 def closed(self):
68 68 return self.stream.closed
69 69
70 70 def close(self):
71 71 pass
72 72
73 73
74 74 class IOTerm:
75 75 """ Term holds the file or file-like objects for handling I/O operations.
76 76
77 77 These are normally just sys.stdin, sys.stdout and sys.stderr but for
78 78 Windows they can can replaced to allow editing the strings before they are
79 79 displayed."""
80 80
81 81 # In the future, having IPython channel all its I/O operations through
82 82 # this class will make it easier to embed it into other environments which
83 83 # are not a normal terminal (such as a GUI-based shell)
84 84 def __init__(self, stdin=None, stdout=None, stderr=None):
85 85 self.stdin = IOStream(stdin, sys.stdin)
86 86 self.stdout = IOStream(stdout, sys.stdout)
87 87 self.stderr = IOStream(stderr, sys.stderr)
88 88
89 89 # setup stdin/stdout/stderr to sys.stdin/sys.stdout/sys.stderr
90 90 stdin = IOStream(sys.stdin)
91 91 stdout = IOStream(sys.stdout)
92 92 stderr = IOStream(sys.stderr)
93 93
94 94
95 95 class Tee(object):
96 96 """A class to duplicate an output stream to stdout/err.
97 97
98 98 This works in a manner very similar to the Unix 'tee' command.
99 99
100 100 When the object is closed or deleted, it closes the original file given to
101 101 it for duplication.
102 102 """
103 103 # Inspired by:
104 104 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
105 105
106 def __init__(self, file_or_name, mode=None, channel='stdout'):
106 def __init__(self, file_or_name, mode="w", channel='stdout'):
107 107 """Construct a new Tee object.
108 108
109 109 Parameters
110 110 ----------
111 111 file_or_name : filename or open filehandle (writable)
112 112 File that will be duplicated
113 113
114 114 mode : optional, valid mode for open().
115 115 If a filename was give, open with this mode.
116 116
117 117 channel : str, one of ['stdout', 'stderr']
118 118 """
119 119 if channel not in ['stdout', 'stderr']:
120 120 raise ValueError('Invalid channel spec %s' % channel)
121 121
122 if hasattr(file, 'write') and hasattr(file, 'seek'):
122 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
123 123 self.file = file_or_name
124 124 else:
125 125 self.file = open(file_or_name, mode)
126 126 self.channel = channel
127 127 self.ostream = getattr(sys, channel)
128 128 setattr(sys, channel, self)
129 129 self._closed = False
130 130
131 131 def close(self):
132 132 """Close the file and restore the channel."""
133 133 self.flush()
134 134 setattr(sys, self.channel, self.ostream)
135 135 self.file.close()
136 136 self._closed = True
137 137
138 138 def write(self, data):
139 139 """Write data to both channels."""
140 140 self.file.write(data)
141 141 self.ostream.write(data)
142 142 self.ostream.flush()
143 143
144 144 def flush(self):
145 145 """Flush both channels."""
146 146 self.file.flush()
147 147 self.ostream.flush()
148 148
149 149 def __del__(self):
150 150 if not self._closed:
151 151 self.close()
152 152
153 153
154 154 def file_read(filename):
155 155 """Read a file and close it. Returns the file source."""
156 156 fobj = open(filename,'r');
157 157 source = fobj.read();
158 158 fobj.close()
159 159 return source
160 160
161 161
162 162 def file_readlines(filename):
163 163 """Read a file and close it. Returns the file source using readlines()."""
164 164 fobj = open(filename,'r');
165 165 lines = fobj.readlines();
166 166 fobj.close()
167 167 return lines
168 168
169 169
170 170 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
171 171 """Take multiple lines of input.
172 172
173 173 A list with each line of input as a separate element is returned when a
174 174 termination string is entered (defaults to a single '.'). Input can also
175 175 terminate via EOF (^D in Unix, ^Z-RET in Windows).
176 176
177 177 Lines of input which end in \\ are joined into single entries (and a
178 178 secondary continuation prompt is issued as long as the user terminates
179 179 lines with \\). This allows entering very long strings which are still
180 180 meant to be treated as single entities.
181 181 """
182 182
183 183 try:
184 184 if header:
185 185 header += '\n'
186 186 lines = [raw_input(header + ps1)]
187 187 except EOFError:
188 188 return []
189 189 terminate = [terminate_str]
190 190 try:
191 191 while lines[-1:] != terminate:
192 192 new_line = raw_input(ps1)
193 193 while new_line.endswith('\\'):
194 194 new_line = new_line[:-1] + raw_input(ps2)
195 195 lines.append(new_line)
196 196
197 197 return lines[:-1] # don't return the termination command
198 198 except EOFError:
199 199 print()
200 200 return lines
201 201
202 202
203 203 def raw_input_ext(prompt='', ps2='... '):
204 204 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
205 205
206 206 line = raw_input(prompt)
207 207 while line.endswith('\\'):
208 208 line = line[:-1] + raw_input(ps2)
209 209 return line
210 210
211 211
212 212 def ask_yes_no(prompt,default=None):
213 213 """Asks a question and returns a boolean (y/n) answer.
214 214
215 215 If default is given (one of 'y','n'), it is used if the user input is
216 216 empty. Otherwise the question is repeated until an answer is given.
217 217
218 218 An EOF is treated as the default answer. If there is no default, an
219 219 exception is raised to prevent infinite loops.
220 220
221 221 Valid answers are: y/yes/n/no (match is not case sensitive)."""
222 222
223 223 answers = {'y':True,'n':False,'yes':True,'no':False}
224 224 ans = None
225 225 while ans not in answers.keys():
226 226 try:
227 227 ans = raw_input(prompt+' ').lower()
228 228 if not ans: # response was an empty string
229 229 ans = default
230 230 except KeyboardInterrupt:
231 231 pass
232 232 except EOFError:
233 233 if default in answers.keys():
234 234 ans = default
235 235 print()
236 236 else:
237 237 raise
238 238
239 239 return answers[ans]
240 240
241 241
242 242 class NLprinter:
243 243 """Print an arbitrarily nested list, indicating index numbers.
244 244
245 245 An instance of this class called nlprint is available and callable as a
246 246 function.
247 247
248 248 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
249 249 and using 'sep' to separate the index from the value. """
250 250
251 251 def __init__(self):
252 252 self.depth = 0
253 253
254 254 def __call__(self,lst,pos='',**kw):
255 255 """Prints the nested list numbering levels."""
256 256 kw.setdefault('indent',' ')
257 257 kw.setdefault('sep',': ')
258 258 kw.setdefault('start',0)
259 259 kw.setdefault('stop',len(lst))
260 260 # we need to remove start and stop from kw so they don't propagate
261 261 # into a recursive call for a nested list.
262 262 start = kw['start']; del kw['start']
263 263 stop = kw['stop']; del kw['stop']
264 264 if self.depth == 0 and 'header' in kw.keys():
265 265 print(kw['header'])
266 266
267 267 for idx in range(start,stop):
268 268 elem = lst[idx]
269 269 newpos = pos + str(idx)
270 270 if type(elem)==type([]):
271 271 self.depth += 1
272 272 self.__call__(elem, newpos+",", **kw)
273 273 self.depth -= 1
274 274 else:
275 275 print(kw['indent']*self.depth + newpos + kw["sep"] + repr(elem))
276 276
277 277 nlprint = NLprinter()
278 278
279 279
280 280 def temp_pyfile(src, ext='.py'):
281 281 """Make a temporary python file, return filename and filehandle.
282 282
283 283 Parameters
284 284 ----------
285 285 src : string or list of strings (no need for ending newlines if list)
286 286 Source code to be written to the file.
287 287
288 288 ext : optional, string
289 289 Extension for the generated file.
290 290
291 291 Returns
292 292 -------
293 293 (filename, open filehandle)
294 294 It is the caller's responsibility to close the open file and unlink it.
295 295 """
296 296 fname = tempfile.mkstemp(ext)[1]
297 297 f = open(fname,'w')
298 298 f.write(src)
299 299 f.flush()
300 300 return fname, f
301 301
302 302
303 303 def raw_print(*args, **kw):
304 304 """Raw print to sys.__stdout__, otherwise identical interface to print()."""
305 305
306 306 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
307 307 file=sys.__stdout__)
308 308 sys.__stdout__.flush()
309 309
310 310
311 311 def raw_print_err(*args, **kw):
312 312 """Raw print to sys.__stderr__, otherwise identical interface to print()."""
313 313
314 314 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
315 315 file=sys.__stderr__)
316 316 sys.__stderr__.flush()
317 317
318 318
319 319 # Short aliases for quick debugging, do NOT use these in production code.
320 320 rprint = raw_print
321 321 rprinte = raw_print_err
General Comments 0
You need to be logged in to leave comments. Login now