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