##// END OF EJS Templates
custom keyboard interrupt handling in ask_yes_no...
Paul Ivanov -
Show More
@@ -1,229 +1,232 b''
1 1 # encoding: utf-8
2 2 """
3 3 IO related utilities.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 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 os
18 18 import sys
19 19 import tempfile
20 20 from .capture import CapturedIO, capture_output
21 21 from .py3compat import string_types, input
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Code
25 25 #-----------------------------------------------------------------------------
26 26
27 27
28 28 class IOStream:
29 29
30 30 def __init__(self,stream, fallback=None):
31 31 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
32 32 if fallback is not None:
33 33 stream = fallback
34 34 else:
35 35 raise ValueError("fallback required, but not specified")
36 36 self.stream = stream
37 37 self._swrite = stream.write
38 38
39 39 # clone all methods not overridden:
40 40 def clone(meth):
41 41 return not hasattr(self, meth) and not meth.startswith('_')
42 42 for meth in filter(clone, dir(stream)):
43 43 setattr(self, meth, getattr(stream, meth))
44 44
45 45 def write(self,data):
46 46 try:
47 47 self._swrite(data)
48 48 except:
49 49 try:
50 50 # print handles some unicode issues which may trip a plain
51 51 # write() call. Emulate write() by using an empty end
52 52 # argument.
53 53 print(data, end='', file=self.stream)
54 54 except:
55 55 # if we get here, something is seriously broken.
56 56 print('ERROR - failed to write data to stream:', self.stream,
57 57 file=sys.stderr)
58 58
59 59 def writelines(self, lines):
60 60 if isinstance(lines, string_types):
61 61 lines = [lines]
62 62 for line in lines:
63 63 self.write(line)
64 64
65 65 # This class used to have a writeln method, but regular files and streams
66 66 # in Python don't have this method. We need to keep this completely
67 67 # compatible so we removed it.
68 68
69 69 @property
70 70 def closed(self):
71 71 return self.stream.closed
72 72
73 73 def close(self):
74 74 pass
75 75
76 76 # setup stdin/stdout/stderr to sys.stdin/sys.stdout/sys.stderr
77 77 devnull = open(os.devnull, 'a')
78 78 stdin = IOStream(sys.stdin, fallback=devnull)
79 79 stdout = IOStream(sys.stdout, fallback=devnull)
80 80 stderr = IOStream(sys.stderr, fallback=devnull)
81 81
82 82 class IOTerm:
83 83 """ Term holds the file or file-like objects for handling I/O operations.
84 84
85 85 These are normally just sys.stdin, sys.stdout and sys.stderr but for
86 86 Windows they can can replaced to allow editing the strings before they are
87 87 displayed."""
88 88
89 89 # In the future, having IPython channel all its I/O operations through
90 90 # this class will make it easier to embed it into other environments which
91 91 # are not a normal terminal (such as a GUI-based shell)
92 92 def __init__(self, stdin=None, stdout=None, stderr=None):
93 93 mymodule = sys.modules[__name__]
94 94 self.stdin = IOStream(stdin, mymodule.stdin)
95 95 self.stdout = IOStream(stdout, mymodule.stdout)
96 96 self.stderr = IOStream(stderr, mymodule.stderr)
97 97
98 98
99 99 class Tee(object):
100 100 """A class to duplicate an output stream to stdout/err.
101 101
102 102 This works in a manner very similar to the Unix 'tee' command.
103 103
104 104 When the object is closed or deleted, it closes the original file given to
105 105 it for duplication.
106 106 """
107 107 # Inspired by:
108 108 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
109 109
110 110 def __init__(self, file_or_name, mode="w", channel='stdout'):
111 111 """Construct a new Tee object.
112 112
113 113 Parameters
114 114 ----------
115 115 file_or_name : filename or open filehandle (writable)
116 116 File that will be duplicated
117 117
118 118 mode : optional, valid mode for open().
119 119 If a filename was give, open with this mode.
120 120
121 121 channel : str, one of ['stdout', 'stderr']
122 122 """
123 123 if channel not in ['stdout', 'stderr']:
124 124 raise ValueError('Invalid channel spec %s' % channel)
125 125
126 126 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
127 127 self.file = file_or_name
128 128 else:
129 129 self.file = open(file_or_name, mode)
130 130 self.channel = channel
131 131 self.ostream = getattr(sys, channel)
132 132 setattr(sys, channel, self)
133 133 self._closed = False
134 134
135 135 def close(self):
136 136 """Close the file and restore the channel."""
137 137 self.flush()
138 138 setattr(sys, self.channel, self.ostream)
139 139 self.file.close()
140 140 self._closed = True
141 141
142 142 def write(self, data):
143 143 """Write data to both channels."""
144 144 self.file.write(data)
145 145 self.ostream.write(data)
146 146 self.ostream.flush()
147 147
148 148 def flush(self):
149 149 """Flush both channels."""
150 150 self.file.flush()
151 151 self.ostream.flush()
152 152
153 153 def __del__(self):
154 154 if not self._closed:
155 155 self.close()
156 156
157 157
158 def ask_yes_no(prompt,default=None):
158 def ask_yes_no(prompt, default=None, interrupt=None):
159 159 """Asks a question and returns a boolean (y/n) answer.
160 160
161 161 If default is given (one of 'y','n'), it is used if the user input is
162 empty. Otherwise the question is repeated until an answer is given.
162 empty. If interrupt is given (one of 'y','n'), it is used if the user
163 presses Ctrl-C. Otherwise the question is repeated until an answer is
164 given.
163 165
164 166 An EOF is treated as the default answer. If there is no default, an
165 167 exception is raised to prevent infinite loops.
166 168
167 169 Valid answers are: y/yes/n/no (match is not case sensitive)."""
168 170
169 171 answers = {'y':True,'n':False,'yes':True,'no':False}
170 172 ans = None
171 173 while ans not in answers.keys():
172 174 try:
173 175 ans = input(prompt+' ').lower()
174 176 if not ans: # response was an empty string
175 177 ans = default
176 178 except KeyboardInterrupt:
177 pass
179 if interrupt:
180 ans = interrupt
178 181 except EOFError:
179 182 if default in answers.keys():
180 183 ans = default
181 184 print()
182 185 else:
183 186 raise
184 187
185 188 return answers[ans]
186 189
187 190
188 191 def temp_pyfile(src, ext='.py'):
189 192 """Make a temporary python file, return filename and filehandle.
190 193
191 194 Parameters
192 195 ----------
193 196 src : string or list of strings (no need for ending newlines if list)
194 197 Source code to be written to the file.
195 198
196 199 ext : optional, string
197 200 Extension for the generated file.
198 201
199 202 Returns
200 203 -------
201 204 (filename, open filehandle)
202 205 It is the caller's responsibility to close the open file and unlink it.
203 206 """
204 207 fname = tempfile.mkstemp(ext)[1]
205 208 f = open(fname,'w')
206 209 f.write(src)
207 210 f.flush()
208 211 return fname, f
209 212
210 213
211 214 def raw_print(*args, **kw):
212 215 """Raw print to sys.__stdout__, otherwise identical interface to print()."""
213 216
214 217 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
215 218 file=sys.__stdout__)
216 219 sys.__stdout__.flush()
217 220
218 221
219 222 def raw_print_err(*args, **kw):
220 223 """Raw print to sys.__stderr__, otherwise identical interface to print()."""
221 224
222 225 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
223 226 file=sys.__stderr__)
224 227 sys.__stderr__.flush()
225 228
226 229
227 230 # Short aliases for quick debugging, do NOT use these in production code.
228 231 rprint = raw_print
229 232 rprinte = raw_print_err
General Comments 0
You need to be logged in to leave comments. Login now