##// END OF EJS Templates
Do not use NamedTemporaryFile in handle_image_tempfile...
Takafumi Arakaki -
Show More
@@ -0,0 +1,20 b''
1 #-----------------------------------------------------------------------------
2 # Copyright (C) 2012- The IPython Development Team
3 #
4 # Distributed under the terms of the BSD License. The full license is in
5 # the file COPYING, distributed as part of this software.
6 #-----------------------------------------------------------------------------
7
8 import os
9
10 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
11
12
13 def test_named_file_in_temporary_directory():
14 with NamedFileInTemporaryDirectory('filename') as file:
15 name = file.name
16 assert not file.closed
17 assert os.path.exists(name)
18 file.write('test')
19 assert file.closed
20 assert not os.path.exists(name)
@@ -1,467 +1,467 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Frontend of ipython working with python-zmq
3 3
4 4 Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
5 5
6 6 For more details, see the ipython-zmq design
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18 from __future__ import print_function
19 19
20 20 import bdb
21 21 import signal
22 22 import os
23 23 import sys
24 24 import time
25 import tempfile
26 25 import subprocess
27 26 from io import BytesIO
28 27 import base64
29 28
30 29 from Queue import Empty
31 30
32 31 try:
33 32 from contextlib import nested
34 33 except:
35 34 from IPython.utils.nested_context import nested
36 35
37 36 from IPython.core.alias import AliasManager, AliasError
38 37 from IPython.core import page
39 38 from IPython.utils.warn import warn, error, fatal
40 39 from IPython.utils import io
41 40 from IPython.utils.traitlets import List, Enum, Any
41 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
42 42
43 43 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
44 44 from IPython.frontend.terminal.console.completer import ZMQCompleter
45 45
46 46
47 47 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
48 48 """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
49 49 _executing = False
50 50
51 51 image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
52 52 config=True, help=
53 53 """
54 54 Handler for image type output. This is useful, for example,
55 55 when connecting to the kernel in which pylab inline backend is
56 56 activated. There are four handlers defined. 'PIL': Use
57 57 Python Imaging Library to popup image; 'stream': Use an
58 58 external program to show the image. Image will be fed into
59 59 the STDIN of the program. You will need to configure
60 60 `stream_image_handler`; 'tempfile': Use an external program to
61 61 show the image. Image will be saved in a temporally file and
62 62 the program is called with the temporally file. You will need
63 63 to configure `tempfile_image_handler`; 'callable': You can set
64 64 any Python callable which is called with the image data. You
65 65 will need to configure `callable_image_handler`.
66 66 """
67 67 )
68 68
69 69 stream_image_handler = List(config=True, help=
70 70 """
71 71 Command to invoke an image viewer program when you are using
72 72 'stream' image handler. This option is a list of string where
73 73 the first element is the command itself and reminders are the
74 74 options for the command. Raw image data is given as STDIN to
75 75 the program.
76 76 """
77 77 )
78 78
79 79 tempfile_image_handler = List(config=True, help=
80 80 """
81 81 Command to invoke an image viewer program when you are using
82 82 'tempfile' image handler. This option is a list of string
83 83 where the first element is the command itself and reminders
84 84 are the options for the command. You can use {file} and
85 85 {format} in the string to represent the location of the
86 86 generated image file and image format.
87 87 """
88 88 )
89 89
90 90 callable_image_handler = Any(config=True, help=
91 91 """
92 92 Callable object called via 'callable' image handler with one
93 93 argument, `data`, which is `msg["content"]["data"]` where
94 94 `msg` is the message from iopub channel. For exmaple, you can
95 95 find base64 encoded PNG data as `data['image/png']`. You can
96 96 use {format} in the string to represent the image format.
97 97 """
98 98 )
99 99
100 100 mime_preference = List(
101 101 default_value=['image/png', 'image/jpeg', 'image/svg+xml'],
102 102 config=True, allow_none=False, help=
103 103 """
104 104 Preferred object representation MIME type in order. First
105 105 matched MIME type will be used.
106 106 """
107 107 )
108 108
109 109 def __init__(self, *args, **kwargs):
110 110 self.km = kwargs.pop('kernel_manager')
111 111 self.session_id = self.km.session.session
112 112 super(ZMQTerminalInteractiveShell, self).__init__(*args, **kwargs)
113 113
114 114 def init_completer(self):
115 115 """Initialize the completion machinery.
116 116
117 117 This creates completion machinery that can be used by client code,
118 118 either interactively in-process (typically triggered by the readline
119 119 library), programatically (such as in test suites) or out-of-prcess
120 120 (typically over the network by remote frontends).
121 121 """
122 122 from IPython.core.completerlib import (module_completer,
123 123 magic_run_completer, cd_completer)
124 124
125 125 self.Completer = ZMQCompleter(self, self.km)
126 126
127 127
128 128 self.set_hook('complete_command', module_completer, str_key = 'import')
129 129 self.set_hook('complete_command', module_completer, str_key = 'from')
130 130 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
131 131 self.set_hook('complete_command', cd_completer, str_key = '%cd')
132 132
133 133 # Only configure readline if we truly are using readline. IPython can
134 134 # do tab-completion over the network, in GUIs, etc, where readline
135 135 # itself may be absent
136 136 if self.has_readline:
137 137 self.set_readline_completer()
138 138
139 139 def run_cell(self, cell, store_history=True):
140 140 """Run a complete IPython cell.
141 141
142 142 Parameters
143 143 ----------
144 144 cell : str
145 145 The code (including IPython code such as %magic functions) to run.
146 146 store_history : bool
147 147 If True, the raw and translated cell will be stored in IPython's
148 148 history. For user code calling back into IPython's machinery, this
149 149 should be set to False.
150 150 """
151 151 if (not cell) or cell.isspace():
152 152 return
153 153
154 154 if cell.strip() == 'exit':
155 155 # explicitly handle 'exit' command
156 156 return self.ask_exit()
157 157
158 158 self._executing = True
159 159 # flush stale replies, which could have been ignored, due to missed heartbeats
160 160 while self.km.shell_channel.msg_ready():
161 161 self.km.shell_channel.get_msg()
162 162 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
163 163 msg_id = self.km.shell_channel.execute(cell, not store_history)
164 164 while not self.km.shell_channel.msg_ready() and self.km.is_alive:
165 165 try:
166 166 self.handle_stdin_request(timeout=0.05)
167 167 except Empty:
168 168 # display intermediate print statements, etc.
169 169 self.handle_iopub()
170 170 pass
171 171 if self.km.shell_channel.msg_ready():
172 172 self.handle_execute_reply(msg_id)
173 173 self._executing = False
174 174
175 175 #-----------------
176 176 # message handlers
177 177 #-----------------
178 178
179 179 def handle_execute_reply(self, msg_id):
180 180 msg = self.km.shell_channel.get_msg()
181 181 if msg["parent_header"].get("msg_id", None) == msg_id:
182 182
183 183 self.handle_iopub()
184 184
185 185 content = msg["content"]
186 186 status = content['status']
187 187
188 188 if status == 'aborted':
189 189 self.write('Aborted\n')
190 190 return
191 191 elif status == 'ok':
192 192 # print execution payloads as well:
193 193 for item in content["payload"]:
194 194 text = item.get('text', None)
195 195 if text:
196 196 page.page(text)
197 197
198 198 elif status == 'error':
199 199 for frame in content["traceback"]:
200 200 print(frame, file=io.stderr)
201 201
202 202 self.execution_count = int(content["execution_count"] + 1)
203 203
204 204
205 205 def handle_iopub(self):
206 206 """ Method to procces subscribe channel's messages
207 207
208 208 This method reads a message and processes the content in different
209 209 outputs like stdout, stderr, pyout and status
210 210
211 211 Arguments:
212 212 sub_msg: message receive from kernel in the sub socket channel
213 213 capture by kernel manager.
214 214 """
215 215 while self.km.sub_channel.msg_ready():
216 216 sub_msg = self.km.sub_channel.get_msg()
217 217 msg_type = sub_msg['header']['msg_type']
218 218 parent = sub_msg["parent_header"]
219 219 if (not parent) or self.session_id == parent['session']:
220 220 if msg_type == 'status' :
221 221 if sub_msg["content"]["execution_state"] == "busy" :
222 222 pass
223 223
224 224 elif msg_type == 'stream' :
225 225 if sub_msg["content"]["name"] == "stdout":
226 226 print(sub_msg["content"]["data"], file=io.stdout, end="")
227 227 io.stdout.flush()
228 228 elif sub_msg["content"]["name"] == "stderr" :
229 229 print(sub_msg["content"]["data"], file=io.stderr, end="")
230 230 io.stderr.flush()
231 231
232 232 elif msg_type == 'pyout':
233 233 self.execution_count = int(sub_msg["content"]["execution_count"])
234 234 format_dict = sub_msg["content"]["data"]
235 235 self.handle_rich_data(format_dict)
236 236 # taken from DisplayHook.__call__:
237 237 hook = self.displayhook
238 238 hook.start_displayhook()
239 239 hook.write_output_prompt()
240 240 hook.write_format_data(format_dict)
241 241 hook.log_output(format_dict)
242 242 hook.finish_displayhook()
243 243
244 244 elif msg_type == 'display_data':
245 245 self.handle_rich_data(sub_msg["content"]["data"])
246 246
247 247 _imagemime = {
248 248 'image/png': 'png',
249 249 'image/jpeg': 'jpeg',
250 250 'image/svg+xml': 'svg',
251 251 }
252 252
253 253 def handle_rich_data(self, data):
254 254 for mime in self.mime_preference:
255 255 if mime in data and mime in self._imagemime:
256 256 self.handle_image(data, mime)
257 257 return
258 258
259 259 def handle_image(self, data, mime):
260 260 handler = getattr(
261 261 self, 'handle_image_{0}'.format(self.image_handler), None)
262 262 if handler:
263 263 handler(data, mime)
264 264
265 265 def handle_image_PIL(self, data, mime):
266 266 if mime not in ('image/png', 'image/jpeg'):
267 267 return
268 268 import PIL
269 269 raw = base64.decodestring(data[mime])
270 270 img = PIL.Image.open(BytesIO(raw))
271 271 img.show()
272 272
273 273 def handle_image_stream(self, data, mime):
274 274 raw = base64.decodestring(data[mime])
275 275 imageformat = self._imagemime[mime]
276 276 fmt = dict(format=imageformat)
277 277 args = [s.format(**fmt) for s in self.stream_image_handler]
278 278 with open(os.devnull, 'w') as devnull:
279 279 proc = subprocess.Popen(
280 280 args, stdin=subprocess.PIPE,
281 281 stdout=devnull, stderr=devnull)
282 282 proc.communicate(raw)
283 283
284 284 def handle_image_tempfile(self, data, mime):
285 285 raw = base64.decodestring(data[mime])
286 286 imageformat = self._imagemime[mime]
287 287 ext = '.{0}'.format(imageformat)
288 with nested(tempfile.NamedTemporaryFile(suffix=ext),
288 with nested(NamedFileInTemporaryDirectory('tmp.{0}'.format(ext)),
289 289 open(os.devnull, 'w')) as (f, devnull):
290 290 f.write(raw)
291 291 f.flush()
292 292 fmt = dict(file=f.name, format=imageformat)
293 293 args = [s.format(**fmt) for s in self.tempfile_image_handler]
294 294 subprocess.call(args, stdout=devnull, stderr=devnull)
295 295
296 296 def handle_image_callable(self, data, mime):
297 297 self.callable_image_handler(data)
298 298
299 299 def handle_stdin_request(self, timeout=0.1):
300 300 """ Method to capture raw_input
301 301 """
302 302 msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
303 303 # in case any iopub came while we were waiting:
304 304 self.handle_iopub()
305 305 if self.session_id == msg_rep["parent_header"].get("session"):
306 306 # wrap SIGINT handler
307 307 real_handler = signal.getsignal(signal.SIGINT)
308 308 def double_int(sig,frame):
309 309 # call real handler (forwards sigint to kernel),
310 310 # then raise local interrupt, stopping local raw_input
311 311 real_handler(sig,frame)
312 312 raise KeyboardInterrupt
313 313 signal.signal(signal.SIGINT, double_int)
314 314
315 315 try:
316 316 raw_data = raw_input(msg_rep["content"]["prompt"])
317 317 except EOFError:
318 318 # turn EOFError into EOF character
319 319 raw_data = '\x04'
320 320 except KeyboardInterrupt:
321 321 sys.stdout.write('\n')
322 322 return
323 323 finally:
324 324 # restore SIGINT handler
325 325 signal.signal(signal.SIGINT, real_handler)
326 326
327 327 # only send stdin reply if there *was not* another request
328 328 # or execution finished while we were reading.
329 329 if not (self.km.stdin_channel.msg_ready() or self.km.shell_channel.msg_ready()):
330 330 self.km.stdin_channel.input(raw_data)
331 331
332 332 def mainloop(self, display_banner=False):
333 333 while True:
334 334 try:
335 335 self.interact(display_banner=display_banner)
336 336 #self.interact_with_readline()
337 337 # XXX for testing of a readline-decoupled repl loop, call
338 338 # interact_with_readline above
339 339 break
340 340 except KeyboardInterrupt:
341 341 # this should not be necessary, but KeyboardInterrupt
342 342 # handling seems rather unpredictable...
343 343 self.write("\nKeyboardInterrupt in interact()\n")
344 344
345 345 def wait_for_kernel(self, timeout=None):
346 346 """method to wait for a kernel to be ready"""
347 347 tic = time.time()
348 348 self.km.hb_channel.unpause()
349 349 while True:
350 350 self.run_cell('1', False)
351 351 if self.km.hb_channel.is_beating():
352 352 # heart failure was not the reason this returned
353 353 break
354 354 else:
355 355 # heart failed
356 356 if timeout is not None and (time.time() - tic) > timeout:
357 357 return False
358 358 return True
359 359
360 360 def interact(self, display_banner=None):
361 361 """Closely emulate the interactive Python console."""
362 362
363 363 # batch run -> do not interact
364 364 if self.exit_now:
365 365 return
366 366
367 367 if display_banner is None:
368 368 display_banner = self.display_banner
369 369
370 370 if isinstance(display_banner, basestring):
371 371 self.show_banner(display_banner)
372 372 elif display_banner:
373 373 self.show_banner()
374 374
375 375 more = False
376 376
377 377 # run a non-empty no-op, so that we don't get a prompt until
378 378 # we know the kernel is ready. This keeps the connection
379 379 # message above the first prompt.
380 380 if not self.wait_for_kernel(3):
381 381 error("Kernel did not respond\n")
382 382 return
383 383
384 384 if self.has_readline:
385 385 self.readline_startup_hook(self.pre_readline)
386 386 hlen_b4_cell = self.readline.get_current_history_length()
387 387 else:
388 388 hlen_b4_cell = 0
389 389 # exit_now is set by a call to %Exit or %Quit, through the
390 390 # ask_exit callback.
391 391
392 392 while not self.exit_now:
393 393 if not self.km.is_alive:
394 394 # kernel died, prompt for action or exit
395 395 action = "restart" if self.km.has_kernel else "wait for restart"
396 396 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
397 397 if ans:
398 398 if self.km.has_kernel:
399 399 self.km.restart_kernel(True)
400 400 self.wait_for_kernel(3)
401 401 else:
402 402 self.exit_now = True
403 403 continue
404 404 try:
405 405 # protect prompt block from KeyboardInterrupt
406 406 # when sitting on ctrl-C
407 407 self.hooks.pre_prompt_hook()
408 408 if more:
409 409 try:
410 410 prompt = self.prompt_manager.render('in2')
411 411 except Exception:
412 412 self.showtraceback()
413 413 if self.autoindent:
414 414 self.rl_do_indent = True
415 415
416 416 else:
417 417 try:
418 418 prompt = self.separate_in + self.prompt_manager.render('in')
419 419 except Exception:
420 420 self.showtraceback()
421 421
422 422 line = self.raw_input(prompt)
423 423 if self.exit_now:
424 424 # quick exit on sys.std[in|out] close
425 425 break
426 426 if self.autoindent:
427 427 self.rl_do_indent = False
428 428
429 429 except KeyboardInterrupt:
430 430 #double-guard against keyboardinterrupts during kbdint handling
431 431 try:
432 432 self.write('\nKeyboardInterrupt\n')
433 433 source_raw = self.input_splitter.source_raw_reset()[1]
434 434 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
435 435 more = False
436 436 except KeyboardInterrupt:
437 437 pass
438 438 except EOFError:
439 439 if self.autoindent:
440 440 self.rl_do_indent = False
441 441 if self.has_readline:
442 442 self.readline_startup_hook(None)
443 443 self.write('\n')
444 444 self.exit()
445 445 except bdb.BdbQuit:
446 446 warn('The Python debugger has exited with a BdbQuit exception.\n'
447 447 'Because of how pdb handles the stack, it is impossible\n'
448 448 'for IPython to properly format this particular exception.\n'
449 449 'IPython will resume normal operation.')
450 450 except:
451 451 # exceptions here are VERY RARE, but they can be triggered
452 452 # asynchronously by signal handlers, for example.
453 453 self.showtraceback()
454 454 else:
455 455 self.input_splitter.push(line)
456 456 more = self.input_splitter.push_accepts_more()
457 457 if (self.SyntaxTB.last_syntax_error and
458 458 self.autoedit_syntax):
459 459 self.edit_syntax_error()
460 460 if not more:
461 461 source_raw = self.input_splitter.source_raw_reset()[1]
462 462 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
463 463 self.run_cell(source_raw)
464 464
465 465
466 466 # Turn off the exit flag, so the mainloop can be restarted if desired
467 467 self.exit_now = False
@@ -1,76 +1,106 b''
1 1 """TemporaryDirectory class, copied from Python 3.2.
2 2
3 3 This is copied from the stdlib and will be standard in Python 3.2 and onwards.
4 4 """
5 5
6 import os as _os
7
6 8 # This code should only be used in Python versions < 3.2, since after that we
7 9 # can rely on the stdlib itself.
8 10 try:
9 11 from tempfile import TemporaryDirectory
10 12
11 13 except ImportError:
12
13 import os as _os
14 14 from tempfile import mkdtemp, template
15 15
16 16 class TemporaryDirectory(object):
17 17 """Create and return a temporary directory. This has the same
18 18 behavior as mkdtemp but can be used as a context manager. For
19 19 example:
20 20
21 21 with TemporaryDirectory() as tmpdir:
22 22 ...
23 23
24 24 Upon exiting the context, the directory and everthing contained
25 25 in it are removed.
26 26 """
27 27
28 28 def __init__(self, suffix="", prefix=template, dir=None):
29 29 self.name = mkdtemp(suffix, prefix, dir)
30 30 self._closed = False
31 31
32 32 def __enter__(self):
33 33 return self.name
34 34
35 35 def cleanup(self):
36 36 if not self._closed:
37 37 self._rmtree(self.name)
38 38 self._closed = True
39 39
40 40 def __exit__(self, exc, value, tb):
41 41 self.cleanup()
42 42
43 43 __del__ = cleanup
44 44
45 45
46 46 # XXX (ncoghlan): The following code attempts to make
47 47 # this class tolerant of the module nulling out process
48 48 # that happens during CPython interpreter shutdown
49 49 # Alas, it doesn't actually manage it. See issue #10188
50 50 _listdir = staticmethod(_os.listdir)
51 51 _path_join = staticmethod(_os.path.join)
52 52 _isdir = staticmethod(_os.path.isdir)
53 53 _remove = staticmethod(_os.remove)
54 54 _rmdir = staticmethod(_os.rmdir)
55 55 _os_error = _os.error
56 56
57 57 def _rmtree(self, path):
58 58 # Essentially a stripped down version of shutil.rmtree. We can't
59 59 # use globals because they may be None'ed out at shutdown.
60 60 for name in self._listdir(path):
61 61 fullname = self._path_join(path, name)
62 62 try:
63 63 isdir = self._isdir(fullname)
64 64 except self._os_error:
65 65 isdir = False
66 66 if isdir:
67 67 self._rmtree(fullname)
68 68 else:
69 69 try:
70 70 self._remove(fullname)
71 71 except self._os_error:
72 72 pass
73 73 try:
74 74 self._rmdir(path)
75 75 except self._os_error:
76 76 pass
77
78
79 class NamedFileInTemporaryDirectory(object):
80
81 def __init__(self, filename, mode='w+b', bufsize=-1, **kwds):
82 """
83 Open a file named `filename` in a temporary directory.
84
85 This context manager is preferred over `NamedTemporaryFile` in
86 stdlib `tempfile` when one needs to reopen the file.
87
88 Arguments `mode` and `bufsize` are passed to `open`.
89 Rest of the arguments are passed to `TemporaryDirectory`.
90
91 """
92 self._tmpdir = TemporaryDirectory(**kwds)
93 path = _os.path.join(self._tmpdir.name, filename)
94 self.file = open(path, mode, bufsize)
95
96 def cleanup(self):
97 self.file.close()
98 self._tmpdir.cleanup()
99
100 __del__ = cleanup
101
102 def __enter__(self):
103 return self.file
104
105 def __exit__(self, type, value, traceback):
106 self.cleanup()
General Comments 0
You need to be logged in to leave comments. Login now