##// END OF EJS Templates
Addressed issues: renamed ZMQHistoryManager (appears to do need to do more than just an Accessor) in IPython/terminal/console/zmqhistory.py; added abc methods; rewrote access methods to load the history from the kernel client; works with 'ipython console' and with 'ipython console --kernel'
Doug Blank -
Show More
@@ -0,0 +1,111 b''
1 """ ZMQ Kernel History accessor and manager. """
2 #-----------------------------------------------------------------------------
3 # Copyright (C) 2010-2011 The IPython Development Team.
4 #
5 # Distributed under the terms of the BSD License.
6 #
7 # The full license is in the file COPYING.txt, distributed with this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 from IPython.core.history import HistoryAccessorBase
15 from IPython.utils import py3compat
16 from IPython.utils.traitlets import Dict, List
17
18 try:
19 from queue import Empty # Py 3
20 except ImportError:
21 from Queue import Empty # Py 2
22
23 class ZMQHistoryManager(HistoryAccessorBase):
24 """History accessor and manager for ZMQ-based kernels"""
25 input_hist_parsed = List([""])
26 output_hist = Dict()
27 dir_hist = List()
28 output_hist_reprs = Dict()
29
30 def __init__(self, client):
31 """
32 Class to load the command-line history from a ZMQ-based kernel,
33 and access the history.
34
35 Parameters
36 ----------
37
38 client: `IPython.kernel.KernelClient`
39 The kernel client in order to request the history.
40 """
41 self.client = client
42
43 def _load_history(self, raw=True, output=False, hist_access_type='range',
44 **kwargs):
45 """
46 Load the history over ZMQ from the kernel. Wraps the history
47 messaging with loop to wait to get history results.
48 """
49 # 'range' (fill in session, start and stop params),
50 # 'tail' (fill in n)
51 # 'search' (fill in pattern param).
52 msg_id = self.client.history(raw=raw, output=output,
53 hist_access_type=hist_access_type,
54 **kwargs)
55 history = []
56 while True:
57 try:
58 reply = self.client.get_shell_msg(timeout=1)
59 except Empty:
60 break
61 else:
62 if reply['parent_header'].get('msg_id') == msg_id:
63 history = reply['content'].get('history', [])
64 break
65 return history
66
67 def writeout_cache(self):
68 """
69 Not needed for ZMQ-based histories.
70 """
71 pass
72
73 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
74 return self._load_history(hist_access_type='tail', n=n, raw=raw,
75 output=output)
76
77 def search(self, pattern="*", raw=True, search_raw=True,
78 output=False, n=None, unique=False):
79 return self._load_history(hist_access_type='search', pattern=pattern,
80 raw=raw, search_raw=search_raw,
81 output=output, n=n, unique=unique)
82
83 def get_range(self, session, start=1, stop=None, raw=True,output=False):
84 return self._load_history(hist_access_type='range', raw=raw,
85 output=output, start=start, stop=stop,
86 session=session)
87
88 def get_range_by_str(self, rangestr, raw=True, output=False):
89 return self._load_history(hist_access_type='range', raw=raw,
90 output=output, rangestr=rangetr)
91
92 def end_session(self):
93 """
94 Nothing to do for ZMQ-based histories.
95 """
96 pass
97
98 def reset(self, new_session=True):
99 """Clear the session history, releasing all object references, and
100 optionally open a new session."""
101 self.output_hist.clear()
102 # The directory history can't be completely empty
103 self.dir_hist[:] = [py3compat.getcwd()]
104
105 if new_session:
106 if self.session_number:
107 self.end_session()
108 self.input_hist_parsed[:] = [""]
109 self.input_hist_raw[:] = [""]
110 self.new_session()
111
@@ -1,955 +1,875 b''
1 1 """ History related magics and functionality """
2 2 #-----------------------------------------------------------------------------
3 3 # Copyright (C) 2010-2011 The IPython Development Team.
4 4 #
5 5 # Distributed under the terms of the BSD License.
6 6 #
7 7 # The full license is in the file COPYING.txt, distributed with this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13 from __future__ import print_function
14 14
15 15 # Stdlib imports
16 16 import atexit
17 17 import datetime
18 import abc
18 19 import os
19 20 import re
20 21 try:
21 22 import sqlite3
22 23 except ImportError:
23 24 try:
24 25 from pysqlite2 import dbapi2 as sqlite3
25 26 except ImportError:
26 27 sqlite3 = None
27 28 import threading
28 29
29 try:
30 from queue import Empty # Py 3
31 except ImportError:
32 from Queue import Empty # Py 2
33
34 30 # Our own packages
35 31 from IPython.config.configurable import Configurable
36 32 from IPython.external.decorator import decorator
37 33 from IPython.utils.decorators import undoc
38 34 from IPython.utils.path import locate_profile
39 35 from IPython.utils import py3compat
36 from IPython.utils.py3compat import with_metaclass
40 37 from IPython.utils.traitlets import (
41 38 Any, Bool, Dict, Instance, Integer, List, Unicode, TraitError,
42 39 )
43 40 from IPython.utils.warn import warn
44 41
45 42 #-----------------------------------------------------------------------------
46 43 # Classes and functions
47 44 #-----------------------------------------------------------------------------
48 45
49 46 @undoc
50 47 class DummyDB(object):
51 48 """Dummy DB that will act as a black hole for history.
52 49
53 50 Only used in the absence of sqlite"""
54 51 def execute(*args, **kwargs):
55 52 return []
56 53
57 54 def commit(self, *args, **kwargs):
58 55 pass
59 56
60 57 def __enter__(self, *args, **kwargs):
61 58 pass
62 59
63 60 def __exit__(self, *args, **kwargs):
64 61 pass
65 62
66 63
67 64 @decorator
68 65 def needs_sqlite(f, self, *a, **kw):
69 66 """Decorator: return an empty list in the absence of sqlite."""
70 67 if sqlite3 is None or not self.enabled:
71 68 return []
72 69 else:
73 70 return f(self, *a, **kw)
74 71
75 72
76 73 if sqlite3 is not None:
77 74 DatabaseError = sqlite3.DatabaseError
78 75 else:
79 76 @undoc
80 77 class DatabaseError(Exception):
81 78 "Dummy exception when sqlite could not be imported. Should never occur."
82 79
83 80 @decorator
84 81 def catch_corrupt_db(f, self, *a, **kw):
85 82 """A decorator which wraps HistoryAccessor method calls to catch errors from
86 83 a corrupt SQLite database, move the old database out of the way, and create
87 84 a new one.
88 85 """
89 86 try:
90 87 return f(self, *a, **kw)
91 88 except DatabaseError:
92 89 if os.path.isfile(self.hist_file):
93 90 # Try to move the file out of the way
94 91 base,ext = os.path.splitext(self.hist_file)
95 92 newpath = base + '-corrupt' + ext
96 93 os.rename(self.hist_file, newpath)
97 94 self.init_db()
98 95 print("ERROR! History file wasn't a valid SQLite database.",
99 96 "It was moved to %s" % newpath, "and a new file created.")
100 97 return []
101 98
102 99 else:
103 100 # The hist_file is probably :memory: or something else.
104 101 raise
105 102
106 103 class HistoryAccessorBase(Configurable):
107 input_hist_parsed = List([""])
108 input_hist_raw = List([""])
109 output_hist = Dict()
110 dir_hist = List()
111 output_hist_reprs = Dict()
112
113 def end_session(self):
114 pass
115
116 def reset(self, new_session=True):
117 """Clear the session history, releasing all object references, and
118 optionally open a new session."""
119 self.output_hist.clear()
120 # The directory history can't be completely empty
121 self.dir_hist[:] = [py3compat.getcwd()]
122
123 if new_session:
124 if self.session_number:
125 self.end_session()
126 self.input_hist_parsed[:] = [""]
127 self.input_hist_raw[:] = [""]
128 self.new_session()
129
130 def new_session(self, conn=None):
131 pass
132
133 def writeout_cache(self):
134 pass
104 """An abstract class for History Accessors """
135 105
106 @abc.abstractmethod
136 107 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
137 return []
108 pass
138 109
110 @abc.abstractmethod
139 111 def search(self, pattern="*", raw=True, search_raw=True,
140 112 output=False, n=None, unique=False):
141 return []
113 pass
142 114
115 @abc.abstractmethod
143 116 def get_range(self, session, start=1, stop=None, raw=True,output=False):
144 return []
145
146 def get_range_by_str(self, rangestr, raw=True, output=False):
147 return []
148
149 def store_inputs(self, line_num, source, source_raw=None):
150 117 pass
151 118
152 def store_output(self, line_num):
119 @abc.abstractmethod
120 def get_range_by_str(self, rangestr, raw=True, output=False):
153 121 pass
154 122
155 123 class HistoryAccessor(HistoryAccessorBase):
156 124 """Access the history database without adding to it.
157 125
158 126 This is intended for use by standalone history tools. IPython shells use
159 127 HistoryManager, below, which is a subclass of this."""
160 128
161 129 # String holding the path to the history file
162 130 hist_file = Unicode(config=True,
163 131 help="""Path to file to use for SQLite history database.
164 132
165 133 By default, IPython will put the history database in the IPython
166 134 profile directory. If you would rather share one history among
167 135 profiles, you can set this value in each, so that they are consistent.
168 136
169 137 Due to an issue with fcntl, SQLite is known to misbehave on some NFS
170 138 mounts. If you see IPython hanging, try setting this to something on a
171 139 local disk, e.g::
172 140
173 141 ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
174 142
175 143 """)
176 144
177 145 enabled = Bool(True, config=True,
178 146 help="""enable the SQLite history
179 147
180 148 set enabled=False to disable the SQLite history,
181 149 in which case there will be no stored history, no SQLite connection,
182 150 and no background saving thread. This may be necessary in some
183 151 threaded environments where IPython is embedded.
184 152 """
185 153 )
186 154
187 155 connection_options = Dict(config=True,
188 156 help="""Options for configuring the SQLite connection
189 157
190 158 These options are passed as keyword args to sqlite3.connect
191 159 when establishing database conenctions.
192 160 """
193 161 )
194 162
195 163 # The SQLite database
196 164 db = Any()
197 165 def _db_changed(self, name, old, new):
198 166 """validate the db, since it can be an Instance of two different types"""
199 167 connection_types = (DummyDB,)
200 168 if sqlite3 is not None:
201 169 connection_types = (DummyDB, sqlite3.Connection)
202 170 if not isinstance(new, connection_types):
203 171 msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % \
204 172 (self.__class__.__name__, new)
205 173 raise TraitError(msg)
206 174
207 175 def __init__(self, profile='default', hist_file=u'', **traits):
208 176 """Create a new history accessor.
209 177
210 178 Parameters
211 179 ----------
212 180 profile : str
213 181 The name of the profile from which to open history.
214 182 hist_file : str
215 183 Path to an SQLite history database stored by IPython. If specified,
216 184 hist_file overrides profile.
217 185 config : :class:`~IPython.config.loader.Config`
218 186 Config object. hist_file can also be set through this.
219 187 """
220 188 # We need a pointer back to the shell for various tasks.
221 189 super(HistoryAccessor, self).__init__(**traits)
222 190 # defer setting hist_file from kwarg until after init,
223 191 # otherwise the default kwarg value would clobber any value
224 192 # set by config
225 193 if hist_file:
226 194 self.hist_file = hist_file
227 195
228 196 if self.hist_file == u'':
229 197 # No one has set the hist_file, yet.
230 198 self.hist_file = self._get_hist_file_name(profile)
231 199
232 200 if sqlite3 is None and self.enabled:
233 201 warn("IPython History requires SQLite, your history will not be saved")
234 202 self.enabled = False
235 203
236 204 self.init_db()
237 205
238 206 def _get_hist_file_name(self, profile='default'):
239 207 """Find the history file for the given profile name.
240 208
241 209 This is overridden by the HistoryManager subclass, to use the shell's
242 210 active profile.
243 211
244 212 Parameters
245 213 ----------
246 214 profile : str
247 215 The name of a profile which has a history file.
248 216 """
249 217 return os.path.join(locate_profile(profile), 'history.sqlite')
250 218
251 219 @catch_corrupt_db
252 220 def init_db(self):
253 221 """Connect to the database, and create tables if necessary."""
254 222 if not self.enabled:
255 223 self.db = DummyDB()
256 224 return
257 225
258 226 # use detect_types so that timestamps return datetime objects
259 227 kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
260 228 kwargs.update(self.connection_options)
261 229 self.db = sqlite3.connect(self.hist_file, **kwargs)
262 230 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
263 231 primary key autoincrement, start timestamp,
264 232 end timestamp, num_cmds integer, remark text)""")
265 233 self.db.execute("""CREATE TABLE IF NOT EXISTS history
266 234 (session integer, line integer, source text, source_raw text,
267 235 PRIMARY KEY (session, line))""")
268 236 # Output history is optional, but ensure the table's there so it can be
269 237 # enabled later.
270 238 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
271 239 (session integer, line integer, output text,
272 240 PRIMARY KEY (session, line))""")
273 241 self.db.commit()
274 242
275 243 def writeout_cache(self):
276 244 """Overridden by HistoryManager to dump the cache before certain
277 245 database lookups."""
278 246 pass
279 247
280 248 ## -------------------------------
281 249 ## Methods for retrieving history:
282 250 ## -------------------------------
283 251 def _run_sql(self, sql, params, raw=True, output=False):
284 252 """Prepares and runs an SQL query for the history database.
285 253
286 254 Parameters
287 255 ----------
288 256 sql : str
289 257 Any filtering expressions to go after SELECT ... FROM ...
290 258 params : tuple
291 259 Parameters passed to the SQL query (to replace "?")
292 260 raw, output : bool
293 261 See :meth:`get_range`
294 262
295 263 Returns
296 264 -------
297 265 Tuples as :meth:`get_range`
298 266 """
299 267 toget = 'source_raw' if raw else 'source'
300 268 sqlfrom = "history"
301 269 if output:
302 270 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
303 271 toget = "history.%s, output_history.output" % toget
304 272 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
305 273 (toget, sqlfrom) + sql, params)
306 274 if output: # Regroup into 3-tuples, and parse JSON
307 275 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
308 276 return cur
309 277
310 278 @needs_sqlite
311 279 @catch_corrupt_db
312 280 def get_session_info(self, session):
313 281 """Get info about a session.
314 282
315 283 Parameters
316 284 ----------
317 285
318 286 session : int
319 287 Session number to retrieve.
320 288
321 289 Returns
322 290 -------
323 291
324 292 session_id : int
325 293 Session ID number
326 294 start : datetime
327 295 Timestamp for the start of the session.
328 296 end : datetime
329 297 Timestamp for the end of the session, or None if IPython crashed.
330 298 num_cmds : int
331 299 Number of commands run, or None if IPython crashed.
332 300 remark : unicode
333 301 A manually set description.
334 302 """
335 303 query = "SELECT * from sessions where session == ?"
336 304 return self.db.execute(query, (session,)).fetchone()
337 305
338 306 @catch_corrupt_db
339 307 def get_last_session_id(self):
340 308 """Get the last session ID currently in the database.
341 309
342 310 Within IPython, this should be the same as the value stored in
343 311 :attr:`HistoryManager.session_number`.
344 312 """
345 313 for record in self.get_tail(n=1, include_latest=True):
346 314 return record[0]
347 315
348 316 @catch_corrupt_db
349 317 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
350 318 """Get the last n lines from the history database.
351 319
352 320 Parameters
353 321 ----------
354 322 n : int
355 323 The number of lines to get
356 324 raw, output : bool
357 325 See :meth:`get_range`
358 326 include_latest : bool
359 327 If False (default), n+1 lines are fetched, and the latest one
360 328 is discarded. This is intended to be used where the function
361 329 is called by a user command, which it should not return.
362 330
363 331 Returns
364 332 -------
365 333 Tuples as :meth:`get_range`
366 334 """
367 335 self.writeout_cache()
368 336 if not include_latest:
369 337 n += 1
370 338 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
371 339 (n,), raw=raw, output=output)
372 340 if not include_latest:
373 341 return reversed(list(cur)[1:])
374 342 return reversed(list(cur))
375 343
376 344 @catch_corrupt_db
377 345 def search(self, pattern="*", raw=True, search_raw=True,
378 346 output=False, n=None, unique=False):
379 347 """Search the database using unix glob-style matching (wildcards
380 348 * and ?).
381 349
382 350 Parameters
383 351 ----------
384 352 pattern : str
385 353 The wildcarded pattern to match when searching
386 354 search_raw : bool
387 355 If True, search the raw input, otherwise, the parsed input
388 356 raw, output : bool
389 357 See :meth:`get_range`
390 358 n : None or int
391 359 If an integer is given, it defines the limit of
392 360 returned entries.
393 361 unique : bool
394 362 When it is true, return only unique entries.
395 363
396 364 Returns
397 365 -------
398 366 Tuples as :meth:`get_range`
399 367 """
400 368 tosearch = "source_raw" if search_raw else "source"
401 369 if output:
402 370 tosearch = "history." + tosearch
403 371 self.writeout_cache()
404 372 sqlform = "WHERE %s GLOB ?" % tosearch
405 373 params = (pattern,)
406 374 if unique:
407 375 sqlform += ' GROUP BY {0}'.format(tosearch)
408 376 if n is not None:
409 377 sqlform += " ORDER BY session DESC, line DESC LIMIT ?"
410 378 params += (n,)
411 379 elif unique:
412 380 sqlform += " ORDER BY session, line"
413 381 cur = self._run_sql(sqlform, params, raw=raw, output=output)
414 382 if n is not None:
415 383 return reversed(list(cur))
416 384 return cur
417 385
418 386 @catch_corrupt_db
419 387 def get_range(self, session, start=1, stop=None, raw=True,output=False):
420 388 """Retrieve input by session.
421 389
422 390 Parameters
423 391 ----------
424 392 session : int
425 393 Session number to retrieve.
426 394 start : int
427 395 First line to retrieve.
428 396 stop : int
429 397 End of line range (excluded from output itself). If None, retrieve
430 398 to the end of the session.
431 399 raw : bool
432 400 If True, return untranslated input
433 401 output : bool
434 402 If True, attempt to include output. This will be 'real' Python
435 403 objects for the current session, or text reprs from previous
436 404 sessions if db_log_output was enabled at the time. Where no output
437 405 is found, None is used.
438 406
439 407 Returns
440 408 -------
441 409 entries
442 410 An iterator over the desired lines. Each line is a 3-tuple, either
443 411 (session, line, input) if output is False, or
444 412 (session, line, (input, output)) if output is True.
445 413 """
446 414 if stop:
447 415 lineclause = "line >= ? AND line < ?"
448 416 params = (session, start, stop)
449 417 else:
450 418 lineclause = "line>=?"
451 419 params = (session, start)
452 420
453 421 return self._run_sql("WHERE session==? AND %s" % lineclause,
454 422 params, raw=raw, output=output)
455 423
456 424 def get_range_by_str(self, rangestr, raw=True, output=False):
457 425 """Get lines of history from a string of ranges, as used by magic
458 426 commands %hist, %save, %macro, etc.
459 427
460 428 Parameters
461 429 ----------
462 430 rangestr : str
463 431 A string specifying ranges, e.g. "5 ~2/1-4". See
464 432 :func:`magic_history` for full details.
465 433 raw, output : bool
466 434 As :meth:`get_range`
467 435
468 436 Returns
469 437 -------
470 438 Tuples as :meth:`get_range`
471 439 """
472 440 for sess, s, e in extract_hist_ranges(rangestr):
473 441 for line in self.get_range(sess, s, e, raw=raw, output=output):
474 442 yield line
475 443
476 444
477 445 class HistoryManager(HistoryAccessor):
478 446 """A class to organize all history-related functionality in one place.
479 447 """
480 448 # Public interface
481 449
482 450 # An instance of the IPython shell we are attached to
483 451 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
484 452 # Lists to hold processed and raw history. These start with a blank entry
485 453 # so that we can index them starting from 1
486 454 input_hist_parsed = List([""])
487 455 input_hist_raw = List([""])
488 456 # A list of directories visited during session
489 457 dir_hist = List()
490 458 def _dir_hist_default(self):
491 459 try:
492 460 return [py3compat.getcwd()]
493 461 except OSError:
494 462 return []
495 463
496 464 # A dict of output history, keyed with ints from the shell's
497 465 # execution count.
498 466 output_hist = Dict()
499 467 # The text/plain repr of outputs.
500 468 output_hist_reprs = Dict()
501 469
502 470 # The number of the current session in the history database
503 471 session_number = Integer()
504 472
505 473 db_log_output = Bool(False, config=True,
506 474 help="Should the history database include output? (default: no)"
507 475 )
508 476 db_cache_size = Integer(0, config=True,
509 477 help="Write to database every x commands (higher values save disk access & power).\n"
510 478 "Values of 1 or less effectively disable caching."
511 479 )
512 480 # The input and output caches
513 481 db_input_cache = List()
514 482 db_output_cache = List()
515 483
516 484 # History saving in separate thread
517 485 save_thread = Instance('IPython.core.history.HistorySavingThread')
518 486 try: # Event is a function returning an instance of _Event...
519 487 save_flag = Instance(threading._Event)
520 488 except AttributeError: # ...until Python 3.3, when it's a class.
521 489 save_flag = Instance(threading.Event)
522 490
523 491 # Private interface
524 492 # Variables used to store the three last inputs from the user. On each new
525 493 # history update, we populate the user's namespace with these, shifted as
526 494 # necessary.
527 495 _i00 = Unicode(u'')
528 496 _i = Unicode(u'')
529 497 _ii = Unicode(u'')
530 498 _iii = Unicode(u'')
531 499
532 500 # A regex matching all forms of the exit command, so that we don't store
533 501 # them in the history (it's annoying to rewind the first entry and land on
534 502 # an exit call).
535 503 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
536 504
537 505 def __init__(self, shell=None, config=None, **traits):
538 506 """Create a new history manager associated with a shell instance.
539 507 """
540 508 # We need a pointer back to the shell for various tasks.
541 509 super(HistoryManager, self).__init__(shell=shell, config=config,
542 510 **traits)
543 511 self.save_flag = threading.Event()
544 512 self.db_input_cache_lock = threading.Lock()
545 513 self.db_output_cache_lock = threading.Lock()
546 514 if self.enabled and self.hist_file != ':memory:':
547 515 self.save_thread = HistorySavingThread(self)
548 516 self.save_thread.start()
549 517
550 518 self.new_session()
551 519
552 520 def _get_hist_file_name(self, profile=None):
553 521 """Get default history file name based on the Shell's profile.
554 522
555 523 The profile parameter is ignored, but must exist for compatibility with
556 524 the parent class."""
557 525 profile_dir = self.shell.profile_dir.location
558 526 return os.path.join(profile_dir, 'history.sqlite')
559 527
560 528 @needs_sqlite
561 529 def new_session(self, conn=None):
562 530 """Get a new session number."""
563 531 if conn is None:
564 532 conn = self.db
565 533
566 534 with conn:
567 535 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
568 536 NULL, "") """, (datetime.datetime.now(),))
569 537 self.session_number = cur.lastrowid
570 538
571 539 def end_session(self):
572 540 """Close the database session, filling in the end time and line count."""
573 541 self.writeout_cache()
574 542 with self.db:
575 543 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
576 544 session==?""", (datetime.datetime.now(),
577 545 len(self.input_hist_parsed)-1, self.session_number))
578 546 self.session_number = 0
579 547
580 548 def name_session(self, name):
581 549 """Give the current session a name in the history database."""
582 550 with self.db:
583 551 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
584 552 (name, self.session_number))
585 553
586 554 def reset(self, new_session=True):
587 555 """Clear the session history, releasing all object references, and
588 556 optionally open a new session."""
589 557 self.output_hist.clear()
590 558 # The directory history can't be completely empty
591 559 self.dir_hist[:] = [py3compat.getcwd()]
592 560
593 561 if new_session:
594 562 if self.session_number:
595 563 self.end_session()
596 564 self.input_hist_parsed[:] = [""]
597 565 self.input_hist_raw[:] = [""]
598 566 self.new_session()
599 567
600 568 # ------------------------------
601 569 # Methods for retrieving history
602 570 # ------------------------------
603 571 def get_session_info(self, session=0):
604 572 """Get info about a session.
605 573
606 574 Parameters
607 575 ----------
608 576
609 577 session : int
610 578 Session number to retrieve. The current session is 0, and negative
611 579 numbers count back from current session, so -1 is the previous session.
612 580
613 581 Returns
614 582 -------
615 583
616 584 session_id : int
617 585 Session ID number
618 586 start : datetime
619 587 Timestamp for the start of the session.
620 588 end : datetime
621 589 Timestamp for the end of the session, or None if IPython crashed.
622 590 num_cmds : int
623 591 Number of commands run, or None if IPython crashed.
624 592 remark : unicode
625 593 A manually set description.
626 594 """
627 595 if session <= 0:
628 596 session += self.session_number
629 597
630 598 return super(HistoryManager, self).get_session_info(session=session)
631 599
632 600 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
633 601 """Get input and output history from the current session. Called by
634 602 get_range, and takes similar parameters."""
635 603 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
636 604
637 605 n = len(input_hist)
638 606 if start < 0:
639 607 start += n
640 608 if not stop or (stop > n):
641 609 stop = n
642 610 elif stop < 0:
643 611 stop += n
644 612
645 613 for i in range(start, stop):
646 614 if output:
647 615 line = (input_hist[i], self.output_hist_reprs.get(i))
648 616 else:
649 617 line = input_hist[i]
650 618 yield (0, i, line)
651 619
652 620 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
653 621 """Retrieve input by session.
654 622
655 623 Parameters
656 624 ----------
657 625 session : int
658 626 Session number to retrieve. The current session is 0, and negative
659 627 numbers count back from current session, so -1 is previous session.
660 628 start : int
661 629 First line to retrieve.
662 630 stop : int
663 631 End of line range (excluded from output itself). If None, retrieve
664 632 to the end of the session.
665 633 raw : bool
666 634 If True, return untranslated input
667 635 output : bool
668 636 If True, attempt to include output. This will be 'real' Python
669 637 objects for the current session, or text reprs from previous
670 638 sessions if db_log_output was enabled at the time. Where no output
671 639 is found, None is used.
672 640
673 641 Returns
674 642 -------
675 643 entries
676 644 An iterator over the desired lines. Each line is a 3-tuple, either
677 645 (session, line, input) if output is False, or
678 646 (session, line, (input, output)) if output is True.
679 647 """
680 648 if session <= 0:
681 649 session += self.session_number
682 650 if session==self.session_number: # Current session
683 651 return self._get_range_session(start, stop, raw, output)
684 652 return super(HistoryManager, self).get_range(session, start, stop, raw,
685 653 output)
686 654
687 655 ## ----------------------------
688 656 ## Methods for storing history:
689 657 ## ----------------------------
690 658 def store_inputs(self, line_num, source, source_raw=None):
691 659 """Store source and raw input in history and create input cache
692 660 variables ``_i*``.
693 661
694 662 Parameters
695 663 ----------
696 664 line_num : int
697 665 The prompt number of this input.
698 666
699 667 source : str
700 668 Python input.
701 669
702 670 source_raw : str, optional
703 671 If given, this is the raw input without any IPython transformations
704 672 applied to it. If not given, ``source`` is used.
705 673 """
706 674 if source_raw is None:
707 675 source_raw = source
708 676 source = source.rstrip('\n')
709 677 source_raw = source_raw.rstrip('\n')
710 678
711 679 # do not store exit/quit commands
712 680 if self._exit_re.match(source_raw.strip()):
713 681 return
714 682
715 683 self.input_hist_parsed.append(source)
716 684 self.input_hist_raw.append(source_raw)
717 685
718 686 with self.db_input_cache_lock:
719 687 self.db_input_cache.append((line_num, source, source_raw))
720 688 # Trigger to flush cache and write to DB.
721 689 if len(self.db_input_cache) >= self.db_cache_size:
722 690 self.save_flag.set()
723 691
724 692 # update the auto _i variables
725 693 self._iii = self._ii
726 694 self._ii = self._i
727 695 self._i = self._i00
728 696 self._i00 = source_raw
729 697
730 698 # hackish access to user namespace to create _i1,_i2... dynamically
731 699 new_i = '_i%s' % line_num
732 700 to_main = {'_i': self._i,
733 701 '_ii': self._ii,
734 702 '_iii': self._iii,
735 703 new_i : self._i00 }
736 704
737 705 if self.shell is not None:
738 706 self.shell.push(to_main, interactive=False)
739 707
740 708 def store_output(self, line_num):
741 709 """If database output logging is enabled, this saves all the
742 710 outputs from the indicated prompt number to the database. It's
743 711 called by run_cell after code has been executed.
744 712
745 713 Parameters
746 714 ----------
747 715 line_num : int
748 716 The line number from which to save outputs
749 717 """
750 718 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
751 719 return
752 720 output = self.output_hist_reprs[line_num]
753 721
754 722 with self.db_output_cache_lock:
755 723 self.db_output_cache.append((line_num, output))
756 724 if self.db_cache_size <= 1:
757 725 self.save_flag.set()
758 726
759 727 def _writeout_input_cache(self, conn):
760 728 with conn:
761 729 for line in self.db_input_cache:
762 730 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
763 731 (self.session_number,)+line)
764 732
765 733 def _writeout_output_cache(self, conn):
766 734 with conn:
767 735 for line in self.db_output_cache:
768 736 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
769 737 (self.session_number,)+line)
770 738
771 739 @needs_sqlite
772 740 def writeout_cache(self, conn=None):
773 741 """Write any entries in the cache to the database."""
774 742 if conn is None:
775 743 conn = self.db
776 744
777 745 with self.db_input_cache_lock:
778 746 try:
779 747 self._writeout_input_cache(conn)
780 748 except sqlite3.IntegrityError:
781 749 self.new_session(conn)
782 750 print("ERROR! Session/line number was not unique in",
783 751 "database. History logging moved to new session",
784 752 self.session_number)
785 753 try:
786 754 # Try writing to the new session. If this fails, don't
787 755 # recurse
788 756 self._writeout_input_cache(conn)
789 757 except sqlite3.IntegrityError:
790 758 pass
791 759 finally:
792 760 self.db_input_cache = []
793 761
794 762 with self.db_output_cache_lock:
795 763 try:
796 764 self._writeout_output_cache(conn)
797 765 except sqlite3.IntegrityError:
798 766 print("!! Session/line number for output was not unique",
799 767 "in database. Output will not be stored.")
800 768 finally:
801 769 self.db_output_cache = []
802 770
803 class KernelHistoryManager(HistoryAccessorBase):
804 def __init__(self, client):
805 self.client = client
806 self._load_history()
807
808 def _load_history(self):
809 msg_id = self.client.history()
810 while True:
811 try:
812 reply = self.client.get_shell_msg(timeout=1)
813 except Empty:
814 break
815 else:
816 if reply['parent_header'].get('msg_id') == msg_id:
817 history = reply['content'].get('history', [])
818 break
819 self.history = history
820 print("_load_history:", self.history)
821
822 def writeout_cache(self):
823 """dump cache before certain database lookups."""
824 print("write_cache")
825
826 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
827 print("get_tail: ", n)
828 return self.history[-n:]
829
830 def search(self, pattern="*", raw=True, search_raw=True,
831 output=False, n=None, unique=False):
832 print("search: ", pattern)
833 return []
834
835 def get_range(self, session, start=1, stop=None, raw=True,output=False):
836 print("get_range: ", start, stop)
837 if stop is None:
838 stop = -1
839 return self.history[start:stop]
840
841 def get_range_by_str(self, rangestr, raw=True, output=False):
842 print("get_range_by_str: " + rangestr)
843 return []
844
845 def store_inputs(self, line_num, source, source_raw=None):
846 print("store_inputs")
847
848 def store_output(self, line_num):
849 print("store_output")
850
851 771
852 772 class HistorySavingThread(threading.Thread):
853 773 """This thread takes care of writing history to the database, so that
854 774 the UI isn't held up while that happens.
855 775
856 776 It waits for the HistoryManager's save_flag to be set, then writes out
857 777 the history cache. The main thread is responsible for setting the flag when
858 778 the cache size reaches a defined threshold."""
859 779 daemon = True
860 780 stop_now = False
861 781 enabled = True
862 782 def __init__(self, history_manager):
863 783 super(HistorySavingThread, self).__init__(name="IPythonHistorySavingThread")
864 784 self.history_manager = history_manager
865 785 self.enabled = history_manager.enabled
866 786 atexit.register(self.stop)
867 787
868 788 @needs_sqlite
869 789 def run(self):
870 790 # We need a separate db connection per thread:
871 791 try:
872 792 self.db = sqlite3.connect(self.history_manager.hist_file,
873 793 **self.history_manager.connection_options
874 794 )
875 795 while True:
876 796 self.history_manager.save_flag.wait()
877 797 if self.stop_now:
878 798 self.db.close()
879 799 return
880 800 self.history_manager.save_flag.clear()
881 801 self.history_manager.writeout_cache(self.db)
882 802 except Exception as e:
883 803 print(("The history saving thread hit an unexpected error (%s)."
884 804 "History will not be written to the database.") % repr(e))
885 805
886 806 def stop(self):
887 807 """This can be called from the main thread to safely stop this thread.
888 808
889 809 Note that it does not attempt to write out remaining history before
890 810 exiting. That should be done by calling the HistoryManager's
891 811 end_session method."""
892 812 self.stop_now = True
893 813 self.history_manager.save_flag.set()
894 814 self.join()
895 815
896 816
897 817 # To match, e.g. ~5/8-~2/3
898 818 range_re = re.compile(r"""
899 819 ((?P<startsess>~?\d+)/)?
900 820 (?P<start>\d+)?
901 821 ((?P<sep>[\-:])
902 822 ((?P<endsess>~?\d+)/)?
903 823 (?P<end>\d+))?
904 824 $""", re.VERBOSE)
905 825
906 826
907 827 def extract_hist_ranges(ranges_str):
908 828 """Turn a string of history ranges into 3-tuples of (session, start, stop).
909 829
910 830 Examples
911 831 --------
912 832 >>> list(extract_hist_ranges("~8/5-~7/4 2"))
913 833 [(-8, 5, None), (-7, 1, 5), (0, 2, 3)]
914 834 """
915 835 for range_str in ranges_str.split():
916 836 rmatch = range_re.match(range_str)
917 837 if not rmatch:
918 838 continue
919 839 start = rmatch.group("start")
920 840 if start:
921 841 start = int(start)
922 842 end = rmatch.group("end")
923 843 # If no end specified, get (a, a + 1)
924 844 end = int(end) if end else start + 1
925 845 else: # start not specified
926 846 if not rmatch.group('startsess'): # no startsess
927 847 continue
928 848 start = 1
929 849 end = None # provide the entire session hist
930 850
931 851 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
932 852 end += 1
933 853 startsess = rmatch.group("startsess") or "0"
934 854 endsess = rmatch.group("endsess") or startsess
935 855 startsess = int(startsess.replace("~","-"))
936 856 endsess = int(endsess.replace("~","-"))
937 857 assert endsess >= startsess, "start session must be earlier than end session"
938 858
939 859 if endsess == startsess:
940 860 yield (startsess, start, end)
941 861 continue
942 862 # Multiple sessions in one range:
943 863 yield (startsess, start, None)
944 864 for sess in range(startsess+1, endsess):
945 865 yield (sess, 1, None)
946 866 yield (endsess, 1, end)
947 867
948 868
949 869 def _format_lineno(session, line):
950 870 """Helper function to format line numbers properly."""
951 871 if session == 0:
952 872 return str(line)
953 873 return "%s#%s" % (session, line)
954 874
955 875
@@ -1,578 +1,578 b''
1 1 # -*- coding: utf-8 -*-
2 2 """terminal client to the IPython kernel"""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from __future__ import print_function
8 8
9 9 import base64
10 10 import bdb
11 11 import signal
12 12 import os
13 13 import sys
14 14 import time
15 15 import subprocess
16 16 from getpass import getpass
17 17 from io import BytesIO
18 18
19 19 try:
20 20 from queue import Empty # Py 3
21 21 except ImportError:
22 22 from Queue import Empty # Py 2
23 23
24 24 from IPython.core import page
25 25 from IPython.core import release
26 from IPython.core.history import KernelHistoryManager
26 from IPython.terminal.console.zmqhistory import ZMQHistoryManager
27 27 from IPython.utils.warn import warn, error
28 28 from IPython.utils import io
29 29 from IPython.utils.py3compat import string_types, input
30 30 from IPython.utils.traitlets import List, Enum, Any, Instance, Unicode, Float, Bool
31 31 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
32 32
33 33 from IPython.terminal.interactiveshell import TerminalInteractiveShell
34 34 from IPython.terminal.console.completer import ZMQCompleter
35 35
36 36 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
37 37 """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
38 38 _executing = False
39 39 _execution_state = Unicode('')
40 40 _pending_clearoutput = False
41 41 kernel_banner = Unicode('')
42 42 kernel_timeout = Float(60, config=True,
43 43 help="""Timeout for giving up on a kernel (in seconds).
44 44
45 45 On first connect and restart, the console tests whether the
46 46 kernel is running and responsive by sending kernel_info_requests.
47 47 This sets the timeout in seconds for how long the kernel can take
48 48 before being presumed dead.
49 49 """
50 50 )
51 51
52 52 image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
53 53 config=True, help=
54 54 """
55 55 Handler for image type output. This is useful, for example,
56 56 when connecting to the kernel in which pylab inline backend is
57 57 activated. There are four handlers defined. 'PIL': Use
58 58 Python Imaging Library to popup image; 'stream': Use an
59 59 external program to show the image. Image will be fed into
60 60 the STDIN of the program. You will need to configure
61 61 `stream_image_handler`; 'tempfile': Use an external program to
62 62 show the image. Image will be saved in a temporally file and
63 63 the program is called with the temporally file. You will need
64 64 to configure `tempfile_image_handler`; 'callable': You can set
65 65 any Python callable which is called with the image data. You
66 66 will need to configure `callable_image_handler`.
67 67 """
68 68 )
69 69
70 70 stream_image_handler = List(config=True, help=
71 71 """
72 72 Command to invoke an image viewer program when you are using
73 73 'stream' image handler. This option is a list of string where
74 74 the first element is the command itself and reminders are the
75 75 options for the command. Raw image data is given as STDIN to
76 76 the program.
77 77 """
78 78 )
79 79
80 80 tempfile_image_handler = List(config=True, help=
81 81 """
82 82 Command to invoke an image viewer program when you are using
83 83 'tempfile' image handler. This option is a list of string
84 84 where the first element is the command itself and reminders
85 85 are the options for the command. You can use {file} and
86 86 {format} in the string to represent the location of the
87 87 generated image file and image format.
88 88 """
89 89 )
90 90
91 91 callable_image_handler = Any(config=True, help=
92 92 """
93 93 Callable object called via 'callable' image handler with one
94 94 argument, `data`, which is `msg["content"]["data"]` where
95 95 `msg` is the message from iopub channel. For exmaple, you can
96 96 find base64 encoded PNG data as `data['image/png']`.
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 manager = Instance('IPython.kernel.KernelManager')
110 110 client = Instance('IPython.kernel.KernelClient')
111 111 def _client_changed(self, name, old, new):
112 112 self.session_id = new.session.session
113 113 session_id = Unicode()
114 114
115 115 def init_completer(self):
116 116 """Initialize the completion machinery.
117 117
118 118 This creates completion machinery that can be used by client code,
119 119 either interactively in-process (typically triggered by the readline
120 120 library), programmatically (such as in test suites) or out-of-process
121 121 (typically over the network by remote frontends).
122 122 """
123 123 from IPython.core.completerlib import (module_completer,
124 124 magic_run_completer, cd_completer)
125 125
126 126 self.Completer = ZMQCompleter(self, self.client, config=self.config)
127 127
128 128
129 129 self.set_hook('complete_command', module_completer, str_key = 'import')
130 130 self.set_hook('complete_command', module_completer, str_key = 'from')
131 131 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
132 132 self.set_hook('complete_command', cd_completer, str_key = '%cd')
133 133
134 134 # Only configure readline if we truly are using readline. IPython can
135 135 # do tab-completion over the network, in GUIs, etc, where readline
136 136 # itself may be absent
137 137 if self.has_readline:
138 138 self.set_readline_completer()
139 139
140 140 def run_cell(self, cell, store_history=True):
141 141 """Run a complete IPython cell.
142 142
143 143 Parameters
144 144 ----------
145 145 cell : str
146 146 The code (including IPython code such as %magic functions) to run.
147 147 store_history : bool
148 148 If True, the raw and translated cell will be stored in IPython's
149 149 history. For user code calling back into IPython's machinery, this
150 150 should be set to False.
151 151 """
152 152 if (not cell) or cell.isspace():
153 153 # pressing enter flushes any pending display
154 154 self.handle_iopub()
155 155 return
156 156
157 157 # flush stale replies, which could have been ignored, due to missed heartbeats
158 158 while self.client.shell_channel.msg_ready():
159 159 self.client.shell_channel.get_msg()
160 160 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
161 161 msg_id = self.client.shell_channel.execute(cell, not store_history)
162 162
163 163 # first thing is wait for any side effects (output, stdin, etc.)
164 164 self._executing = True
165 165 self._execution_state = "busy"
166 166 while self._execution_state != 'idle' and self.client.is_alive():
167 167 try:
168 168 self.handle_input_request(msg_id, timeout=0.05)
169 169 except Empty:
170 170 # display intermediate print statements, etc.
171 171 self.handle_iopub(msg_id)
172 172
173 173 # after all of that is done, wait for the execute reply
174 174 while self.client.is_alive():
175 175 try:
176 176 self.handle_execute_reply(msg_id, timeout=0.05)
177 177 except Empty:
178 178 pass
179 179 else:
180 180 break
181 181 self._executing = False
182 182
183 183 #-----------------
184 184 # message handlers
185 185 #-----------------
186 186
187 187 def handle_execute_reply(self, msg_id, timeout=None):
188 188 msg = self.client.shell_channel.get_msg(block=False, timeout=timeout)
189 189 if msg["parent_header"].get("msg_id", None) == msg_id:
190 190
191 191 self.handle_iopub(msg_id)
192 192
193 193 content = msg["content"]
194 194 status = content['status']
195 195
196 196 if status == 'aborted':
197 197 self.write('Aborted\n')
198 198 return
199 199 elif status == 'ok':
200 200 # handle payloads
201 201 for item in content["payload"]:
202 202 source = item['source']
203 203 if source == 'page':
204 204 page.page(item['data']['text/plain'])
205 205 elif source == 'set_next_input':
206 206 self.set_next_input(item['text'])
207 207 elif source == 'ask_exit':
208 208 self.ask_exit()
209 209
210 210 elif status == 'error':
211 211 for frame in content["traceback"]:
212 212 print(frame, file=io.stderr)
213 213
214 214 self.execution_count = int(content["execution_count"] + 1)
215 215
216 216 include_other_output = Bool(False, config=True,
217 217 help="""Whether to include output from clients
218 218 other than this one sharing the same kernel.
219 219
220 220 Outputs are not displayed until enter is pressed.
221 221 """
222 222 )
223 223 other_output_prefix = Unicode("[remote] ", config=True,
224 224 help="""Prefix to add to outputs coming from clients other than this one.
225 225
226 226 Only relevant if include_other_output is True.
227 227 """
228 228 )
229 229
230 230 def from_here(self, msg):
231 231 """Return whether a message is from this session"""
232 232 return msg['parent_header'].get("session", self.session_id) == self.session_id
233 233
234 234 def include_output(self, msg):
235 235 """Return whether we should include a given output message"""
236 236 from_here = self.from_here(msg)
237 237 if msg['msg_type'] == 'execute_input':
238 238 # only echo inputs not from here
239 239 return self.include_other_output and not from_here
240 240
241 241 if self.include_other_output:
242 242 return True
243 243 else:
244 244 return from_here
245 245
246 246 def handle_iopub(self, msg_id=''):
247 247 """Process messages on the IOPub channel
248 248
249 249 This method consumes and processes messages on the IOPub channel,
250 250 such as stdout, stderr, execute_result and status.
251 251
252 252 It only displays output that is caused by this session.
253 253 """
254 254 while self.client.iopub_channel.msg_ready():
255 255 sub_msg = self.client.iopub_channel.get_msg()
256 256 msg_type = sub_msg['header']['msg_type']
257 257 parent = sub_msg["parent_header"]
258 258
259 259 if self.include_output(sub_msg):
260 260 if msg_type == 'status':
261 261 self._execution_state = sub_msg["content"]["execution_state"]
262 262 elif msg_type == 'stream':
263 263 if sub_msg["content"]["name"] == "stdout":
264 264 if self._pending_clearoutput:
265 265 print("\r", file=io.stdout, end="")
266 266 self._pending_clearoutput = False
267 267 print(sub_msg["content"]["text"], file=io.stdout, end="")
268 268 io.stdout.flush()
269 269 elif sub_msg["content"]["name"] == "stderr":
270 270 if self._pending_clearoutput:
271 271 print("\r", file=io.stderr, end="")
272 272 self._pending_clearoutput = False
273 273 print(sub_msg["content"]["text"], file=io.stderr, end="")
274 274 io.stderr.flush()
275 275
276 276 elif msg_type == 'execute_result':
277 277 if self._pending_clearoutput:
278 278 print("\r", file=io.stdout, end="")
279 279 self._pending_clearoutput = False
280 280 self.execution_count = int(sub_msg["content"]["execution_count"])
281 281 if not self.from_here(sub_msg):
282 282 sys.stdout.write(self.other_output_prefix)
283 283 format_dict = sub_msg["content"]["data"]
284 284 self.handle_rich_data(format_dict)
285 285
286 286 # taken from DisplayHook.__call__:
287 287 hook = self.displayhook
288 288 hook.start_displayhook()
289 289 hook.write_output_prompt()
290 290 hook.write_format_data(format_dict)
291 291 hook.log_output(format_dict)
292 292 hook.finish_displayhook()
293 293
294 294 elif msg_type == 'display_data':
295 295 data = sub_msg["content"]["data"]
296 296 handled = self.handle_rich_data(data)
297 297 if not handled:
298 298 if not self.from_here(sub_msg):
299 299 sys.stdout.write(self.other_output_prefix)
300 300 # if it was an image, we handled it by now
301 301 if 'text/plain' in data:
302 302 print(data['text/plain'])
303 303
304 304 elif msg_type == 'execute_input':
305 305 content = sub_msg['content']
306 306 self.execution_count = content['execution_count']
307 307 if not self.from_here(sub_msg):
308 308 sys.stdout.write(self.other_output_prefix)
309 309 sys.stdout.write(self.prompt_manager.render('in'))
310 310 sys.stdout.write(content['code'])
311 311
312 312 elif msg_type == 'clear_output':
313 313 if sub_msg["content"]["wait"]:
314 314 self._pending_clearoutput = True
315 315 else:
316 316 print("\r", file=io.stdout, end="")
317 317
318 318 _imagemime = {
319 319 'image/png': 'png',
320 320 'image/jpeg': 'jpeg',
321 321 'image/svg+xml': 'svg',
322 322 }
323 323
324 324 def handle_rich_data(self, data):
325 325 for mime in self.mime_preference:
326 326 if mime in data and mime in self._imagemime:
327 327 self.handle_image(data, mime)
328 328 return True
329 329
330 330 def handle_image(self, data, mime):
331 331 handler = getattr(
332 332 self, 'handle_image_{0}'.format(self.image_handler), None)
333 333 if handler:
334 334 handler(data, mime)
335 335
336 336 def handle_image_PIL(self, data, mime):
337 337 if mime not in ('image/png', 'image/jpeg'):
338 338 return
339 339 import PIL.Image
340 340 raw = base64.decodestring(data[mime].encode('ascii'))
341 341 img = PIL.Image.open(BytesIO(raw))
342 342 img.show()
343 343
344 344 def handle_image_stream(self, data, mime):
345 345 raw = base64.decodestring(data[mime].encode('ascii'))
346 346 imageformat = self._imagemime[mime]
347 347 fmt = dict(format=imageformat)
348 348 args = [s.format(**fmt) for s in self.stream_image_handler]
349 349 with open(os.devnull, 'w') as devnull:
350 350 proc = subprocess.Popen(
351 351 args, stdin=subprocess.PIPE,
352 352 stdout=devnull, stderr=devnull)
353 353 proc.communicate(raw)
354 354
355 355 def handle_image_tempfile(self, data, mime):
356 356 raw = base64.decodestring(data[mime].encode('ascii'))
357 357 imageformat = self._imagemime[mime]
358 358 filename = 'tmp.{0}'.format(imageformat)
359 359 with NamedFileInTemporaryDirectory(filename) as f, \
360 360 open(os.devnull, 'w') as devnull:
361 361 f.write(raw)
362 362 f.flush()
363 363 fmt = dict(file=f.name, format=imageformat)
364 364 args = [s.format(**fmt) for s in self.tempfile_image_handler]
365 365 subprocess.call(args, stdout=devnull, stderr=devnull)
366 366
367 367 def handle_image_callable(self, data, mime):
368 368 self.callable_image_handler(data)
369 369
370 370 def handle_input_request(self, msg_id, timeout=0.1):
371 371 """ Method to capture raw_input
372 372 """
373 373 req = self.client.stdin_channel.get_msg(timeout=timeout)
374 374 # in case any iopub came while we were waiting:
375 375 self.handle_iopub(msg_id)
376 376 if msg_id == req["parent_header"].get("msg_id"):
377 377 # wrap SIGINT handler
378 378 real_handler = signal.getsignal(signal.SIGINT)
379 379 def double_int(sig,frame):
380 380 # call real handler (forwards sigint to kernel),
381 381 # then raise local interrupt, stopping local raw_input
382 382 real_handler(sig,frame)
383 383 raise KeyboardInterrupt
384 384 signal.signal(signal.SIGINT, double_int)
385 385 content = req['content']
386 386 read = getpass if content.get('password', False) else input
387 387 try:
388 388 raw_data = read(content["prompt"])
389 389 except EOFError:
390 390 # turn EOFError into EOF character
391 391 raw_data = '\x04'
392 392 except KeyboardInterrupt:
393 393 sys.stdout.write('\n')
394 394 return
395 395 finally:
396 396 # restore SIGINT handler
397 397 signal.signal(signal.SIGINT, real_handler)
398 398
399 399 # only send stdin reply if there *was not* another request
400 400 # or execution finished while we were reading.
401 401 if not (self.client.stdin_channel.msg_ready() or self.client.shell_channel.msg_ready()):
402 402 self.client.stdin_channel.input(raw_data)
403 403
404 404 def mainloop(self, display_banner=False):
405 405 while True:
406 406 try:
407 407 self.interact(display_banner=display_banner)
408 408 #self.interact_with_readline()
409 409 # XXX for testing of a readline-decoupled repl loop, call
410 410 # interact_with_readline above
411 411 break
412 412 except KeyboardInterrupt:
413 413 # this should not be necessary, but KeyboardInterrupt
414 414 # handling seems rather unpredictable...
415 415 self.write("\nKeyboardInterrupt in interact()\n")
416 416
417 417 self.client.shell_channel.shutdown()
418 418
419 419 def _banner1_default(self):
420 420 return "IPython Console {version}\n".format(version=release.version)
421 421
422 422 def compute_banner(self):
423 423 super(ZMQTerminalInteractiveShell, self).compute_banner()
424 424 if self.client and not self.kernel_banner:
425 425 msg_id = self.client.kernel_info()
426 426 while True:
427 427 try:
428 428 reply = self.client.get_shell_msg(timeout=1)
429 429 except Empty:
430 430 break
431 431 else:
432 432 if reply['parent_header'].get('msg_id') == msg_id:
433 433 self.kernel_banner = reply['content'].get('banner', '')
434 434 break
435 435 self.banner += self.kernel_banner
436 436
437 437 def wait_for_kernel(self, timeout=None):
438 438 """method to wait for a kernel to be ready"""
439 439 tic = time.time()
440 440 self.client.hb_channel.unpause()
441 441 while True:
442 442 msg_id = self.client.kernel_info()
443 443 reply = None
444 444 while True:
445 445 try:
446 446 reply = self.client.get_shell_msg(timeout=1)
447 447 except Empty:
448 448 break
449 449 else:
450 450 if reply['parent_header'].get('msg_id') == msg_id:
451 451 return True
452 452 if timeout is not None \
453 453 and (time.time() - tic) > timeout \
454 454 and not self.client.hb_channel.is_beating():
455 455 # heart failed
456 456 return False
457 457 return True
458 458
459 459 def interact(self, display_banner=None):
460 460 """Closely emulate the interactive Python console."""
461 461
462 462 # batch run -> do not interact
463 463 if self.exit_now:
464 464 return
465 465
466 466 if display_banner is None:
467 467 display_banner = self.display_banner
468 468
469 469 if isinstance(display_banner, string_types):
470 470 self.show_banner(display_banner)
471 471 elif display_banner:
472 472 self.show_banner()
473 473
474 474 more = False
475 475
476 476 # run a non-empty no-op, so that we don't get a prompt until
477 477 # we know the kernel is ready. This keeps the connection
478 478 # message above the first prompt.
479 479 if not self.wait_for_kernel(self.kernel_timeout):
480 480 error("Kernel did not respond\n")
481 481 return
482 482
483 483 if self.has_readline:
484 484 self.readline_startup_hook(self.pre_readline)
485 485 hlen_b4_cell = self.readline.get_current_history_length()
486 486 else:
487 487 hlen_b4_cell = 0
488 488 # exit_now is set by a call to %Exit or %Quit, through the
489 489 # ask_exit callback.
490 490
491 491 while not self.exit_now:
492 492 if not self.client.is_alive():
493 493 # kernel died, prompt for action or exit
494 494
495 495 action = "restart" if self.manager else "wait for restart"
496 496 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
497 497 if ans:
498 498 if self.manager:
499 499 self.manager.restart_kernel(True)
500 500 self.wait_for_kernel(self.kernel_timeout)
501 501 else:
502 502 self.exit_now = True
503 503 continue
504 504 try:
505 505 # protect prompt block from KeyboardInterrupt
506 506 # when sitting on ctrl-C
507 507 self.hooks.pre_prompt_hook()
508 508 if more:
509 509 try:
510 510 prompt = self.prompt_manager.render('in2')
511 511 except Exception:
512 512 self.showtraceback()
513 513 if self.autoindent:
514 514 self.rl_do_indent = True
515 515
516 516 else:
517 517 try:
518 518 prompt = self.separate_in + self.prompt_manager.render('in')
519 519 except Exception:
520 520 self.showtraceback()
521 521
522 522 line = self.raw_input(prompt)
523 523 if self.exit_now:
524 524 # quick exit on sys.std[in|out] close
525 525 break
526 526 if self.autoindent:
527 527 self.rl_do_indent = False
528 528
529 529 except KeyboardInterrupt:
530 530 #double-guard against keyboardinterrupts during kbdint handling
531 531 try:
532 532 self.write('\nKeyboardInterrupt\n')
533 533 source_raw = self.input_splitter.raw_reset()
534 534 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
535 535 more = False
536 536 except KeyboardInterrupt:
537 537 pass
538 538 except EOFError:
539 539 if self.autoindent:
540 540 self.rl_do_indent = False
541 541 if self.has_readline:
542 542 self.readline_startup_hook(None)
543 543 self.write('\n')
544 544 self.exit()
545 545 except bdb.BdbQuit:
546 546 warn('The Python debugger has exited with a BdbQuit exception.\n'
547 547 'Because of how pdb handles the stack, it is impossible\n'
548 548 'for IPython to properly format this particular exception.\n'
549 549 'IPython will resume normal operation.')
550 550 except:
551 551 # exceptions here are VERY RARE, but they can be triggered
552 552 # asynchronously by signal handlers, for example.
553 553 self.showtraceback()
554 554 else:
555 555 try:
556 556 self.input_splitter.push(line)
557 557 more = self.input_splitter.push_accepts_more()
558 558 except SyntaxError:
559 559 # Run the code directly - run_cell takes care of displaying
560 560 # the exception.
561 561 more = False
562 562 if (self.SyntaxTB.last_syntax_error and
563 563 self.autoedit_syntax):
564 564 self.edit_syntax_error()
565 565 if not more:
566 566 source_raw = self.input_splitter.raw_reset()
567 567 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
568 568 self.run_cell(source_raw)
569 569
570 570
571 571 # Turn off the exit flag, so the mainloop can be restarted if desired
572 572 self.exit_now = False
573 573
574 574 def init_history(self):
575 575 """Sets up the command history. """
576 self.history_manager = KernelHistoryManager(client=self.client)
576 self.history_manager = ZMQHistoryManager(client=self.client)
577 577 self.configurables.append(self.history_manager)
578 578
General Comments 0
You need to be logged in to leave comments. Login now