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