##// END OF EJS Templates
Move cast_unicode_py2 calls into HistoryAccessor
Thomas Kluyver -
Show More
@@ -1,911 +1,912 b''
1 1 """ History related magics and functionality """
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 from __future__ import print_function
7 7
8 8 import atexit
9 9 import datetime
10 10 import os
11 11 import re
12 12 try:
13 13 import sqlite3
14 14 except ImportError:
15 15 try:
16 16 from pysqlite2 import dbapi2 as sqlite3
17 17 except ImportError:
18 18 sqlite3 = None
19 19 import threading
20 20
21 21 from traitlets.config.configurable import LoggingConfigurable
22 22 from decorator import decorator
23 23 from IPython.utils.decorators import undoc
24 24 from IPython.utils.path import locate_profile
25 25 from IPython.utils import py3compat
26 26 from traitlets import (
27 27 Any, Bool, Dict, Instance, Integer, List, Unicode, TraitError,
28 28 default, observe,
29 29 )
30 30 from warnings import warn
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Classes and functions
34 34 #-----------------------------------------------------------------------------
35 35
36 36 @undoc
37 37 class DummyDB(object):
38 38 """Dummy DB that will act as a black hole for history.
39 39
40 40 Only used in the absence of sqlite"""
41 41 def execute(*args, **kwargs):
42 42 return []
43 43
44 44 def commit(self, *args, **kwargs):
45 45 pass
46 46
47 47 def __enter__(self, *args, **kwargs):
48 48 pass
49 49
50 50 def __exit__(self, *args, **kwargs):
51 51 pass
52 52
53 53
54 54 @decorator
55 55 def needs_sqlite(f, self, *a, **kw):
56 56 """Decorator: return an empty list in the absence of sqlite."""
57 57 if sqlite3 is None or not self.enabled:
58 58 return []
59 59 else:
60 60 return f(self, *a, **kw)
61 61
62 62
63 63 if sqlite3 is not None:
64 64 DatabaseError = sqlite3.DatabaseError
65 65 OperationalError = sqlite3.OperationalError
66 66 else:
67 67 @undoc
68 68 class DatabaseError(Exception):
69 69 "Dummy exception when sqlite could not be imported. Should never occur."
70 70
71 71 @undoc
72 72 class OperationalError(Exception):
73 73 "Dummy exception when sqlite could not be imported. Should never occur."
74 74
75 75 # use 16kB as threshold for whether a corrupt history db should be saved
76 76 # that should be at least 100 entries or so
77 77 _SAVE_DB_SIZE = 16384
78 78
79 79 @decorator
80 80 def catch_corrupt_db(f, self, *a, **kw):
81 81 """A decorator which wraps HistoryAccessor method calls to catch errors from
82 82 a corrupt SQLite database, move the old database out of the way, and create
83 83 a new one.
84 84
85 85 We avoid clobbering larger databases because this may be triggered due to filesystem issues,
86 86 not just a corrupt file.
87 87 """
88 88 try:
89 89 return f(self, *a, **kw)
90 90 except (DatabaseError, OperationalError) as e:
91 91 self._corrupt_db_counter += 1
92 92 self.log.error("Failed to open SQLite history %s (%s).", self.hist_file, e)
93 93 if self.hist_file != ':memory:':
94 94 if self._corrupt_db_counter > self._corrupt_db_limit:
95 95 self.hist_file = ':memory:'
96 96 self.log.error("Failed to load history too many times, history will not be saved.")
97 97 elif os.path.isfile(self.hist_file):
98 98 # move the file out of the way
99 99 base, ext = os.path.splitext(self.hist_file)
100 100 size = os.stat(self.hist_file).st_size
101 101 if size >= _SAVE_DB_SIZE:
102 102 # if there's significant content, avoid clobbering
103 103 now = datetime.datetime.now().isoformat().replace(':', '.')
104 104 newpath = base + '-corrupt-' + now + ext
105 105 # don't clobber previous corrupt backups
106 106 for i in range(100):
107 107 if not os.path.isfile(newpath):
108 108 break
109 109 else:
110 110 newpath = base + '-corrupt-' + now + (u'-%i' % i) + ext
111 111 else:
112 112 # not much content, possibly empty; don't worry about clobbering
113 113 # maybe we should just delete it?
114 114 newpath = base + '-corrupt' + ext
115 115 os.rename(self.hist_file, newpath)
116 116 self.log.error("History file was moved to %s and a new file created.", newpath)
117 117 self.init_db()
118 118 return []
119 119 else:
120 120 # Failed with :memory:, something serious is wrong
121 121 raise
122 122
123 123 class HistoryAccessorBase(LoggingConfigurable):
124 124 """An abstract class for History Accessors """
125 125
126 126 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
127 127 raise NotImplementedError
128 128
129 129 def search(self, pattern="*", raw=True, search_raw=True,
130 130 output=False, n=None, unique=False):
131 131 raise NotImplementedError
132 132
133 133 def get_range(self, session, start=1, stop=None, raw=True,output=False):
134 134 raise NotImplementedError
135 135
136 136 def get_range_by_str(self, rangestr, raw=True, output=False):
137 137 raise NotImplementedError
138 138
139 139
140 140 class HistoryAccessor(HistoryAccessorBase):
141 141 """Access the history database without adding to it.
142 142
143 143 This is intended for use by standalone history tools. IPython shells use
144 144 HistoryManager, below, which is a subclass of this."""
145 145
146 146 # counter for init_db retries, so we don't keep trying over and over
147 147 _corrupt_db_counter = 0
148 148 # after two failures, fallback on :memory:
149 149 _corrupt_db_limit = 2
150 150
151 151 # String holding the path to the history file
152 152 hist_file = Unicode(
153 153 help="""Path to file to use for SQLite history database.
154 154
155 155 By default, IPython will put the history database in the IPython
156 156 profile directory. If you would rather share one history among
157 157 profiles, you can set this value in each, so that they are consistent.
158 158
159 159 Due to an issue with fcntl, SQLite is known to misbehave on some NFS
160 160 mounts. If you see IPython hanging, try setting this to something on a
161 161 local disk, e.g::
162 162
163 163 ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
164 164
165 165 you can also use the specific value `:memory:` (including the colon
166 166 at both end but not the back ticks), to avoid creating an history file.
167 167
168 168 """).tag(config=True)
169 169
170 170 enabled = Bool(True,
171 171 help="""enable the SQLite history
172 172
173 173 set enabled=False to disable the SQLite history,
174 174 in which case there will be no stored history, no SQLite connection,
175 175 and no background saving thread. This may be necessary in some
176 176 threaded environments where IPython is embedded.
177 177 """
178 178 ).tag(config=True)
179 179
180 180 connection_options = Dict(
181 181 help="""Options for configuring the SQLite connection
182 182
183 183 These options are passed as keyword args to sqlite3.connect
184 184 when establishing database conenctions.
185 185 """
186 186 ).tag(config=True)
187 187
188 188 # The SQLite database
189 189 db = Any()
190 190 @observe('db')
191 191 def _db_changed(self, change):
192 192 """validate the db, since it can be an Instance of two different types"""
193 193 new = change['new']
194 194 connection_types = (DummyDB,)
195 195 if sqlite3 is not None:
196 196 connection_types = (DummyDB, sqlite3.Connection)
197 197 if not isinstance(new, connection_types):
198 198 msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % \
199 199 (self.__class__.__name__, new)
200 200 raise TraitError(msg)
201 201
202 202 def __init__(self, profile='default', hist_file=u'', **traits):
203 203 """Create a new history accessor.
204 204
205 205 Parameters
206 206 ----------
207 207 profile : str
208 208 The name of the profile from which to open history.
209 209 hist_file : str
210 210 Path to an SQLite history database stored by IPython. If specified,
211 211 hist_file overrides profile.
212 212 config : :class:`~traitlets.config.loader.Config`
213 213 Config object. hist_file can also be set through this.
214 214 """
215 215 # We need a pointer back to the shell for various tasks.
216 216 super(HistoryAccessor, self).__init__(**traits)
217 217 # defer setting hist_file from kwarg until after init,
218 218 # otherwise the default kwarg value would clobber any value
219 219 # set by config
220 220 if hist_file:
221 221 self.hist_file = hist_file
222 222
223 223 if self.hist_file == u'':
224 224 # No one has set the hist_file, yet.
225 225 self.hist_file = self._get_hist_file_name(profile)
226 226
227 227 if sqlite3 is None and self.enabled:
228 228 warn("IPython History requires SQLite, your history will not be saved")
229 229 self.enabled = False
230 230
231 231 self.init_db()
232 232
233 233 def _get_hist_file_name(self, profile='default'):
234 234 """Find the history file for the given profile name.
235 235
236 236 This is overridden by the HistoryManager subclass, to use the shell's
237 237 active profile.
238 238
239 239 Parameters
240 240 ----------
241 241 profile : str
242 242 The name of a profile which has a history file.
243 243 """
244 244 return os.path.join(locate_profile(profile), 'history.sqlite')
245 245
246 246 @catch_corrupt_db
247 247 def init_db(self):
248 248 """Connect to the database, and create tables if necessary."""
249 249 if not self.enabled:
250 250 self.db = DummyDB()
251 251 return
252 252
253 253 # use detect_types so that timestamps return datetime objects
254 254 kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
255 255 kwargs.update(self.connection_options)
256 256 self.db = sqlite3.connect(self.hist_file, **kwargs)
257 257 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
258 258 primary key autoincrement, start timestamp,
259 259 end timestamp, num_cmds integer, remark text)""")
260 260 self.db.execute("""CREATE TABLE IF NOT EXISTS history
261 261 (session integer, line integer, source text, source_raw text,
262 262 PRIMARY KEY (session, line))""")
263 263 # Output history is optional, but ensure the table's there so it can be
264 264 # enabled later.
265 265 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
266 266 (session integer, line integer, output text,
267 267 PRIMARY KEY (session, line))""")
268 268 self.db.commit()
269 269 # success! reset corrupt db count
270 270 self._corrupt_db_counter = 0
271 271
272 272 def writeout_cache(self):
273 273 """Overridden by HistoryManager to dump the cache before certain
274 274 database lookups."""
275 275 pass
276 276
277 277 ## -------------------------------
278 278 ## Methods for retrieving history:
279 279 ## -------------------------------
280 280 def _run_sql(self, sql, params, raw=True, output=False):
281 281 """Prepares and runs an SQL query for the history database.
282 282
283 283 Parameters
284 284 ----------
285 285 sql : str
286 286 Any filtering expressions to go after SELECT ... FROM ...
287 287 params : tuple
288 288 Parameters passed to the SQL query (to replace "?")
289 289 raw, output : bool
290 290 See :meth:`get_range`
291 291
292 292 Returns
293 293 -------
294 294 Tuples as :meth:`get_range`
295 295 """
296 296 toget = 'source_raw' if raw else 'source'
297 297 sqlfrom = "history"
298 298 if output:
299 299 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
300 300 toget = "history.%s, output_history.output" % toget
301 301 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
302 302 (toget, sqlfrom) + sql, params)
303 303 if output: # Regroup into 3-tuples, and parse JSON
304 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
304 return ((ses, lin, (py3compat.cast_unicode_py2(inp), py3compat.cast_unicode_py2(out)))
305 for ses, lin, inp, out in cur)
305 306 return cur
306 307
307 308 @needs_sqlite
308 309 @catch_corrupt_db
309 310 def get_session_info(self, session):
310 311 """Get info about a session.
311 312
312 313 Parameters
313 314 ----------
314 315
315 316 session : int
316 317 Session number to retrieve.
317 318
318 319 Returns
319 320 -------
320 321
321 322 session_id : int
322 323 Session ID number
323 324 start : datetime
324 325 Timestamp for the start of the session.
325 326 end : datetime
326 327 Timestamp for the end of the session, or None if IPython crashed.
327 328 num_cmds : int
328 329 Number of commands run, or None if IPython crashed.
329 330 remark : unicode
330 331 A manually set description.
331 332 """
332 333 query = "SELECT * from sessions where session == ?"
333 334 return self.db.execute(query, (session,)).fetchone()
334 335
335 336 @catch_corrupt_db
336 337 def get_last_session_id(self):
337 338 """Get the last session ID currently in the database.
338 339
339 340 Within IPython, this should be the same as the value stored in
340 341 :attr:`HistoryManager.session_number`.
341 342 """
342 343 for record in self.get_tail(n=1, include_latest=True):
343 344 return record[0]
344 345
345 346 @catch_corrupt_db
346 347 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
347 348 """Get the last n lines from the history database.
348 349
349 350 Parameters
350 351 ----------
351 352 n : int
352 353 The number of lines to get
353 354 raw, output : bool
354 355 See :meth:`get_range`
355 356 include_latest : bool
356 357 If False (default), n+1 lines are fetched, and the latest one
357 358 is discarded. This is intended to be used where the function
358 359 is called by a user command, which it should not return.
359 360
360 361 Returns
361 362 -------
362 363 Tuples as :meth:`get_range`
363 364 """
364 365 self.writeout_cache()
365 366 if not include_latest:
366 367 n += 1
367 368 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
368 369 (n,), raw=raw, output=output)
369 370 if not include_latest:
370 371 return reversed(list(cur)[1:])
371 372 return reversed(list(cur))
372 373
373 374 @catch_corrupt_db
374 375 def search(self, pattern="*", raw=True, search_raw=True,
375 376 output=False, n=None, unique=False):
376 377 """Search the database using unix glob-style matching (wildcards
377 378 * and ?).
378 379
379 380 Parameters
380 381 ----------
381 382 pattern : str
382 383 The wildcarded pattern to match when searching
383 384 search_raw : bool
384 385 If True, search the raw input, otherwise, the parsed input
385 386 raw, output : bool
386 387 See :meth:`get_range`
387 388 n : None or int
388 389 If an integer is given, it defines the limit of
389 390 returned entries.
390 391 unique : bool
391 392 When it is true, return only unique entries.
392 393
393 394 Returns
394 395 -------
395 396 Tuples as :meth:`get_range`
396 397 """
397 398 tosearch = "source_raw" if search_raw else "source"
398 399 if output:
399 400 tosearch = "history." + tosearch
400 401 self.writeout_cache()
401 402 sqlform = "WHERE %s GLOB ?" % tosearch
402 403 params = (pattern,)
403 404 if unique:
404 405 sqlform += ' GROUP BY {0}'.format(tosearch)
405 406 if n is not None:
406 407 sqlform += " ORDER BY session DESC, line DESC LIMIT ?"
407 408 params += (n,)
408 409 elif unique:
409 410 sqlform += " ORDER BY session, line"
410 411 cur = self._run_sql(sqlform, params, raw=raw, output=output)
411 412 if n is not None:
412 413 return reversed(list(cur))
413 414 return cur
414 415
415 416 @catch_corrupt_db
416 417 def get_range(self, session, start=1, stop=None, raw=True,output=False):
417 418 """Retrieve input by session.
418 419
419 420 Parameters
420 421 ----------
421 422 session : int
422 423 Session number to retrieve.
423 424 start : int
424 425 First line to retrieve.
425 426 stop : int
426 427 End of line range (excluded from output itself). If None, retrieve
427 428 to the end of the session.
428 429 raw : bool
429 430 If True, return untranslated input
430 431 output : bool
431 432 If True, attempt to include output. This will be 'real' Python
432 433 objects for the current session, or text reprs from previous
433 434 sessions if db_log_output was enabled at the time. Where no output
434 435 is found, None is used.
435 436
436 437 Returns
437 438 -------
438 439 entries
439 440 An iterator over the desired lines. Each line is a 3-tuple, either
440 441 (session, line, input) if output is False, or
441 442 (session, line, (input, output)) if output is True.
442 443 """
443 444 if stop:
444 445 lineclause = "line >= ? AND line < ?"
445 446 params = (session, start, stop)
446 447 else:
447 448 lineclause = "line>=?"
448 449 params = (session, start)
449 450
450 451 return self._run_sql("WHERE session==? AND %s" % lineclause,
451 452 params, raw=raw, output=output)
452 453
453 454 def get_range_by_str(self, rangestr, raw=True, output=False):
454 455 """Get lines of history from a string of ranges, as used by magic
455 456 commands %hist, %save, %macro, etc.
456 457
457 458 Parameters
458 459 ----------
459 460 rangestr : str
460 461 A string specifying ranges, e.g. "5 ~2/1-4". See
461 462 :func:`magic_history` for full details.
462 463 raw, output : bool
463 464 As :meth:`get_range`
464 465
465 466 Returns
466 467 -------
467 468 Tuples as :meth:`get_range`
468 469 """
469 470 for sess, s, e in extract_hist_ranges(rangestr):
470 471 for line in self.get_range(sess, s, e, raw=raw, output=output):
471 472 yield line
472 473
473 474
474 475 class HistoryManager(HistoryAccessor):
475 476 """A class to organize all history-related functionality in one place.
476 477 """
477 478 # Public interface
478 479
479 480 # An instance of the IPython shell we are attached to
480 481 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
481 482 allow_none=True)
482 483 # Lists to hold processed and raw history. These start with a blank entry
483 484 # so that we can index them starting from 1
484 485 input_hist_parsed = List([""])
485 486 input_hist_raw = List([""])
486 487 # A list of directories visited during session
487 488 dir_hist = List()
488 489 @default('dir_hist')
489 490 def _dir_hist_default(self):
490 491 try:
491 492 return [py3compat.getcwd()]
492 493 except OSError:
493 494 return []
494 495
495 496 # A dict of output history, keyed with ints from the shell's
496 497 # execution count.
497 498 output_hist = Dict()
498 499 # The text/plain repr of outputs.
499 500 output_hist_reprs = Dict()
500 501
501 502 # The number of the current session in the history database
502 503 session_number = Integer()
503 504
504 505 db_log_output = Bool(False,
505 506 help="Should the history database include output? (default: no)"
506 507 ).tag(config=True)
507 508 db_cache_size = Integer(0,
508 509 help="Write to database every x commands (higher values save disk access & power).\n"
509 510 "Values of 1 or less effectively disable caching."
510 511 ).tag(config=True)
511 512 # The input and output caches
512 513 db_input_cache = List()
513 514 db_output_cache = List()
514 515
515 516 # History saving in separate thread
516 517 save_thread = Instance('IPython.core.history.HistorySavingThread',
517 518 allow_none=True)
518 519 try: # Event is a function returning an instance of _Event...
519 520 save_flag = Instance(threading._Event, allow_none=True)
520 521 except AttributeError: # ...until Python 3.3, when it's a class.
521 522 save_flag = Instance(threading.Event, allow_none=True)
522 523
523 524 # Private interface
524 525 # Variables used to store the three last inputs from the user. On each new
525 526 # history update, we populate the user's namespace with these, shifted as
526 527 # necessary.
527 528 _i00 = Unicode(u'')
528 529 _i = Unicode(u'')
529 530 _ii = Unicode(u'')
530 531 _iii = Unicode(u'')
531 532
532 533 # A regex matching all forms of the exit command, so that we don't store
533 534 # them in the history (it's annoying to rewind the first entry and land on
534 535 # an exit call).
535 536 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
536 537
537 538 def __init__(self, shell=None, config=None, **traits):
538 539 """Create a new history manager associated with a shell instance.
539 540 """
540 541 # We need a pointer back to the shell for various tasks.
541 542 super(HistoryManager, self).__init__(shell=shell, config=config,
542 543 **traits)
543 544 self.save_flag = threading.Event()
544 545 self.db_input_cache_lock = threading.Lock()
545 546 self.db_output_cache_lock = threading.Lock()
546 547
547 548 try:
548 549 self.new_session()
549 550 except OperationalError:
550 551 self.log.error("Failed to create history session in %s. History will not be saved.",
551 552 self.hist_file, exc_info=True)
552 553 self.hist_file = ':memory:'
553 554
554 555 if self.enabled and self.hist_file != ':memory:':
555 556 self.save_thread = HistorySavingThread(self)
556 557 self.save_thread.start()
557 558
558 559 def _get_hist_file_name(self, profile=None):
559 560 """Get default history file name based on the Shell's profile.
560 561
561 562 The profile parameter is ignored, but must exist for compatibility with
562 563 the parent class."""
563 564 profile_dir = self.shell.profile_dir.location
564 565 return os.path.join(profile_dir, 'history.sqlite')
565 566
566 567 @needs_sqlite
567 568 def new_session(self, conn=None):
568 569 """Get a new session number."""
569 570 if conn is None:
570 571 conn = self.db
571 572
572 573 with conn:
573 574 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
574 575 NULL, "") """, (datetime.datetime.now(),))
575 576 self.session_number = cur.lastrowid
576 577
577 578 def end_session(self):
578 579 """Close the database session, filling in the end time and line count."""
579 580 self.writeout_cache()
580 581 with self.db:
581 582 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
582 583 session==?""", (datetime.datetime.now(),
583 584 len(self.input_hist_parsed)-1, self.session_number))
584 585 self.session_number = 0
585 586
586 587 def name_session(self, name):
587 588 """Give the current session a name in the history database."""
588 589 with self.db:
589 590 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
590 591 (name, self.session_number))
591 592
592 593 def reset(self, new_session=True):
593 594 """Clear the session history, releasing all object references, and
594 595 optionally open a new session."""
595 596 self.output_hist.clear()
596 597 # The directory history can't be completely empty
597 598 self.dir_hist[:] = [py3compat.getcwd()]
598 599
599 600 if new_session:
600 601 if self.session_number:
601 602 self.end_session()
602 603 self.input_hist_parsed[:] = [""]
603 604 self.input_hist_raw[:] = [""]
604 605 self.new_session()
605 606
606 607 # ------------------------------
607 608 # Methods for retrieving history
608 609 # ------------------------------
609 610 def get_session_info(self, session=0):
610 611 """Get info about a session.
611 612
612 613 Parameters
613 614 ----------
614 615
615 616 session : int
616 617 Session number to retrieve. The current session is 0, and negative
617 618 numbers count back from current session, so -1 is the previous session.
618 619
619 620 Returns
620 621 -------
621 622
622 623 session_id : int
623 624 Session ID number
624 625 start : datetime
625 626 Timestamp for the start of the session.
626 627 end : datetime
627 628 Timestamp for the end of the session, or None if IPython crashed.
628 629 num_cmds : int
629 630 Number of commands run, or None if IPython crashed.
630 631 remark : unicode
631 632 A manually set description.
632 633 """
633 634 if session <= 0:
634 635 session += self.session_number
635 636
636 637 return super(HistoryManager, self).get_session_info(session=session)
637 638
638 639 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
639 640 """Get input and output history from the current session. Called by
640 641 get_range, and takes similar parameters."""
641 642 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
642 643
643 644 n = len(input_hist)
644 645 if start < 0:
645 646 start += n
646 647 if not stop or (stop > n):
647 648 stop = n
648 649 elif stop < 0:
649 650 stop += n
650 651
651 652 for i in range(start, stop):
652 653 if output:
653 654 line = (input_hist[i], self.output_hist_reprs.get(i))
654 655 else:
655 656 line = input_hist[i]
656 657 yield (0, i, line)
657 658
658 659 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
659 660 """Retrieve input by session.
660 661
661 662 Parameters
662 663 ----------
663 664 session : int
664 665 Session number to retrieve. The current session is 0, and negative
665 666 numbers count back from current session, so -1 is previous session.
666 667 start : int
667 668 First line to retrieve.
668 669 stop : int
669 670 End of line range (excluded from output itself). If None, retrieve
670 671 to the end of the session.
671 672 raw : bool
672 673 If True, return untranslated input
673 674 output : bool
674 675 If True, attempt to include output. This will be 'real' Python
675 676 objects for the current session, or text reprs from previous
676 677 sessions if db_log_output was enabled at the time. Where no output
677 678 is found, None is used.
678 679
679 680 Returns
680 681 -------
681 682 entries
682 683 An iterator over the desired lines. Each line is a 3-tuple, either
683 684 (session, line, input) if output is False, or
684 685 (session, line, (input, output)) if output is True.
685 686 """
686 687 if session <= 0:
687 688 session += self.session_number
688 689 if session==self.session_number: # Current session
689 690 return self._get_range_session(start, stop, raw, output)
690 691 return super(HistoryManager, self).get_range(session, start, stop, raw,
691 692 output)
692 693
693 694 ## ----------------------------
694 695 ## Methods for storing history:
695 696 ## ----------------------------
696 697 def store_inputs(self, line_num, source, source_raw=None):
697 698 """Store source and raw input in history and create input cache
698 699 variables ``_i*``.
699 700
700 701 Parameters
701 702 ----------
702 703 line_num : int
703 704 The prompt number of this input.
704 705
705 706 source : str
706 707 Python input.
707 708
708 709 source_raw : str, optional
709 710 If given, this is the raw input without any IPython transformations
710 711 applied to it. If not given, ``source`` is used.
711 712 """
712 713 if source_raw is None:
713 714 source_raw = source
714 715 source = source.rstrip('\n')
715 716 source_raw = source_raw.rstrip('\n')
716 717
717 718 # do not store exit/quit commands
718 719 if self._exit_re.match(source_raw.strip()):
719 720 return
720 721
721 722 self.input_hist_parsed.append(source)
722 723 self.input_hist_raw.append(source_raw)
723 724
724 725 with self.db_input_cache_lock:
725 726 self.db_input_cache.append((line_num, source, source_raw))
726 727 # Trigger to flush cache and write to DB.
727 728 if len(self.db_input_cache) >= self.db_cache_size:
728 729 self.save_flag.set()
729 730
730 731 # update the auto _i variables
731 732 self._iii = self._ii
732 733 self._ii = self._i
733 734 self._i = self._i00
734 735 self._i00 = source_raw
735 736
736 737 # hackish access to user namespace to create _i1,_i2... dynamically
737 738 new_i = '_i%s' % line_num
738 739 to_main = {'_i': self._i,
739 740 '_ii': self._ii,
740 741 '_iii': self._iii,
741 742 new_i : self._i00 }
742 743
743 744 if self.shell is not None:
744 745 self.shell.push(to_main, interactive=False)
745 746
746 747 def store_output(self, line_num):
747 748 """If database output logging is enabled, this saves all the
748 749 outputs from the indicated prompt number to the database. It's
749 750 called by run_cell after code has been executed.
750 751
751 752 Parameters
752 753 ----------
753 754 line_num : int
754 755 The line number from which to save outputs
755 756 """
756 757 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
757 758 return
758 759 output = self.output_hist_reprs[line_num]
759 760
760 761 with self.db_output_cache_lock:
761 762 self.db_output_cache.append((line_num, output))
762 763 if self.db_cache_size <= 1:
763 764 self.save_flag.set()
764 765
765 766 def _writeout_input_cache(self, conn):
766 767 with conn:
767 768 for line in self.db_input_cache:
768 769 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
769 770 (self.session_number,)+line)
770 771
771 772 def _writeout_output_cache(self, conn):
772 773 with conn:
773 774 for line in self.db_output_cache:
774 775 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
775 776 (self.session_number,)+line)
776 777
777 778 @needs_sqlite
778 779 def writeout_cache(self, conn=None):
779 780 """Write any entries in the cache to the database."""
780 781 if conn is None:
781 782 conn = self.db
782 783
783 784 with self.db_input_cache_lock:
784 785 try:
785 786 self._writeout_input_cache(conn)
786 787 except sqlite3.IntegrityError:
787 788 self.new_session(conn)
788 789 print("ERROR! Session/line number was not unique in",
789 790 "database. History logging moved to new session",
790 791 self.session_number)
791 792 try:
792 793 # Try writing to the new session. If this fails, don't
793 794 # recurse
794 795 self._writeout_input_cache(conn)
795 796 except sqlite3.IntegrityError:
796 797 pass
797 798 finally:
798 799 self.db_input_cache = []
799 800
800 801 with self.db_output_cache_lock:
801 802 try:
802 803 self._writeout_output_cache(conn)
803 804 except sqlite3.IntegrityError:
804 805 print("!! Session/line number for output was not unique",
805 806 "in database. Output will not be stored.")
806 807 finally:
807 808 self.db_output_cache = []
808 809
809 810
810 811 class HistorySavingThread(threading.Thread):
811 812 """This thread takes care of writing history to the database, so that
812 813 the UI isn't held up while that happens.
813 814
814 815 It waits for the HistoryManager's save_flag to be set, then writes out
815 816 the history cache. The main thread is responsible for setting the flag when
816 817 the cache size reaches a defined threshold."""
817 818 daemon = True
818 819 stop_now = False
819 820 enabled = True
820 821 def __init__(self, history_manager):
821 822 super(HistorySavingThread, self).__init__(name="IPythonHistorySavingThread")
822 823 self.history_manager = history_manager
823 824 self.enabled = history_manager.enabled
824 825 atexit.register(self.stop)
825 826
826 827 @needs_sqlite
827 828 def run(self):
828 829 # We need a separate db connection per thread:
829 830 try:
830 831 self.db = sqlite3.connect(self.history_manager.hist_file,
831 832 **self.history_manager.connection_options
832 833 )
833 834 while True:
834 835 self.history_manager.save_flag.wait()
835 836 if self.stop_now:
836 837 self.db.close()
837 838 return
838 839 self.history_manager.save_flag.clear()
839 840 self.history_manager.writeout_cache(self.db)
840 841 except Exception as e:
841 842 print(("The history saving thread hit an unexpected error (%s)."
842 843 "History will not be written to the database.") % repr(e))
843 844
844 845 def stop(self):
845 846 """This can be called from the main thread to safely stop this thread.
846 847
847 848 Note that it does not attempt to write out remaining history before
848 849 exiting. That should be done by calling the HistoryManager's
849 850 end_session method."""
850 851 self.stop_now = True
851 852 self.history_manager.save_flag.set()
852 853 self.join()
853 854
854 855
855 856 # To match, e.g. ~5/8-~2/3
856 857 range_re = re.compile(r"""
857 858 ((?P<startsess>~?\d+)/)?
858 859 (?P<start>\d+)?
859 860 ((?P<sep>[\-:])
860 861 ((?P<endsess>~?\d+)/)?
861 862 (?P<end>\d+))?
862 863 $""", re.VERBOSE)
863 864
864 865
865 866 def extract_hist_ranges(ranges_str):
866 867 """Turn a string of history ranges into 3-tuples of (session, start, stop).
867 868
868 869 Examples
869 870 --------
870 871 >>> list(extract_hist_ranges("~8/5-~7/4 2"))
871 872 [(-8, 5, None), (-7, 1, 5), (0, 2, 3)]
872 873 """
873 874 for range_str in ranges_str.split():
874 875 rmatch = range_re.match(range_str)
875 876 if not rmatch:
876 877 continue
877 878 start = rmatch.group("start")
878 879 if start:
879 880 start = int(start)
880 881 end = rmatch.group("end")
881 882 # If no end specified, get (a, a + 1)
882 883 end = int(end) if end else start + 1
883 884 else: # start not specified
884 885 if not rmatch.group('startsess'): # no startsess
885 886 continue
886 887 start = 1
887 888 end = None # provide the entire session hist
888 889
889 890 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
890 891 end += 1
891 892 startsess = rmatch.group("startsess") or "0"
892 893 endsess = rmatch.group("endsess") or startsess
893 894 startsess = int(startsess.replace("~","-"))
894 895 endsess = int(endsess.replace("~","-"))
895 896 assert endsess >= startsess, "start session must be earlier than end session"
896 897
897 898 if endsess == startsess:
898 899 yield (startsess, start, end)
899 900 continue
900 901 # Multiple sessions in one range:
901 902 yield (startsess, start, None)
902 903 for sess in range(startsess+1, endsess):
903 904 yield (sess, 1, None)
904 905 yield (endsess, 1, end)
905 906
906 907
907 908 def _format_lineno(session, line):
908 909 """Helper function to format line numbers properly."""
909 910 if session == 0:
910 911 return str(line)
911 912 return "%s#%s" % (session, line)
@@ -1,543 +1,543 b''
1 1 """IPython terminal interface using prompt_toolkit"""
2 2 from __future__ import print_function
3 3
4 4 import os
5 5 import sys
6 6 import warnings
7 7 from warnings import warn
8 8
9 9 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
10 10 from IPython.utils import io
11 11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input, string_types
12 12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 13 from IPython.utils.process import abbrev_cwd
14 14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union
15 15
16 16 from prompt_toolkit.document import Document
17 17 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
18 18 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
19 19 from prompt_toolkit.history import InMemoryHistory
20 20 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
21 21 from prompt_toolkit.interface import CommandLineInterface
22 22 from prompt_toolkit.key_binding.manager import KeyBindingManager
23 23 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
24 24 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
25 25
26 26 from pygments.styles import get_style_by_name, get_all_styles
27 27 from pygments.style import Style
28 28 from pygments.token import Token
29 29
30 30 from .debugger import TerminalPdb, Pdb
31 31 from .magics import TerminalMagics
32 32 from .pt_inputhooks import get_inputhook_name_and_func
33 33 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
34 34 from .ptutils import IPythonPTCompleter, IPythonPTLexer
35 35 from .shortcuts import register_ipython_shortcuts
36 36
37 37 DISPLAY_BANNER_DEPRECATED = object()
38 38
39 39
40 40 from pygments.style import Style
41 41
42 42 class _NoStyle(Style): pass
43 43
44 44
45 45
46 46 _style_overrides_light_bg = {
47 47 Token.Prompt: '#0000ff',
48 48 Token.PromptNum: '#0000ee bold',
49 49 Token.OutPrompt: '#cc0000',
50 50 Token.OutPromptNum: '#bb0000 bold',
51 51 }
52 52
53 53 _style_overrides_linux = {
54 54 Token.Prompt: '#00cc00',
55 55 Token.PromptNum: '#00bb00 bold',
56 56 Token.OutPrompt: '#cc0000',
57 57 Token.OutPromptNum: '#bb0000 bold',
58 58 }
59 59
60 60
61 61
62 62 def get_default_editor():
63 63 try:
64 64 ed = os.environ['EDITOR']
65 65 if not PY3:
66 66 ed = ed.decode()
67 67 return ed
68 68 except KeyError:
69 69 pass
70 70 except UnicodeError:
71 71 warn("$EDITOR environment variable is not pure ASCII. Using platform "
72 72 "default editor.")
73 73
74 74 if os.name == 'posix':
75 75 return 'vi' # the only one guaranteed to be there!
76 76 else:
77 77 return 'notepad' # same in Windows!
78 78
79 79 # conservatively check for tty
80 80 # overridden streams can result in things like:
81 81 # - sys.stdin = None
82 82 # - no isatty method
83 83 for _name in ('stdin', 'stdout', 'stderr'):
84 84 _stream = getattr(sys, _name)
85 85 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
86 86 _is_tty = False
87 87 break
88 88 else:
89 89 _is_tty = True
90 90
91 91
92 92 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
93 93
94 94 class TerminalInteractiveShell(InteractiveShell):
95 95 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
96 96 'to reserve for the completion menu'
97 97 ).tag(config=True)
98 98
99 99 def _space_for_menu_changed(self, old, new):
100 100 self._update_layout()
101 101
102 102 pt_cli = None
103 103 debugger_history = None
104 104 _pt_app = None
105 105
106 106 simple_prompt = Bool(_use_simple_prompt,
107 107 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
108 108
109 109 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
110 110 IPython own testing machinery, and emacs inferior-shell integration through elpy.
111 111
112 112 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
113 113 environment variable is set, or the current terminal is not a tty.
114 114
115 115 """
116 116 ).tag(config=True)
117 117
118 118 @property
119 119 def debugger_cls(self):
120 120 return Pdb if self.simple_prompt else TerminalPdb
121 121
122 122 confirm_exit = Bool(True,
123 123 help="""
124 124 Set to confirm when you try to exit IPython with an EOF (Control-D
125 125 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
126 126 you can force a direct exit without any confirmation.""",
127 127 ).tag(config=True)
128 128
129 129 editing_mode = Unicode('emacs',
130 130 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
131 131 ).tag(config=True)
132 132
133 133 mouse_support = Bool(False,
134 134 help="Enable mouse support in the prompt"
135 135 ).tag(config=True)
136 136
137 137 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
138 138 help="""The name or class of a Pygments style to use for syntax
139 139 highlighting: \n %s""" % ', '.join(get_all_styles())
140 140 ).tag(config=True)
141 141
142 142
143 143 @observe('highlighting_style')
144 144 @observe('colors')
145 145 def _highlighting_style_changed(self, change):
146 146 self.refresh_style()
147 147
148 148 def refresh_style(self):
149 149 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
150 150
151 151
152 152 highlighting_style_overrides = Dict(
153 153 help="Override highlighting format for specific tokens"
154 154 ).tag(config=True)
155 155
156 156 true_color = Bool(False,
157 157 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
158 158 "If your terminal supports true color, the following command "
159 159 "should print 'TRUECOLOR' in orange: "
160 160 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
161 161 ).tag(config=True)
162 162
163 163 editor = Unicode(get_default_editor(),
164 164 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
165 165 ).tag(config=True)
166 166
167 167 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
168 168
169 169 prompts = Instance(Prompts)
170 170
171 171 @default('prompts')
172 172 def _prompts_default(self):
173 173 return self.prompts_class(self)
174 174
175 175 @observe('prompts')
176 176 def _(self, change):
177 177 self._update_layout()
178 178
179 179 @default('displayhook_class')
180 180 def _displayhook_class_default(self):
181 181 return RichPromptDisplayHook
182 182
183 183 term_title = Bool(True,
184 184 help="Automatically set the terminal title"
185 185 ).tag(config=True)
186 186
187 187 display_completions = Enum(('column', 'multicolumn','readlinelike'),
188 188 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
189 189 "'readlinelike'. These options are for `prompt_toolkit`, see "
190 190 "`prompt_toolkit` documentation for more information."
191 191 ),
192 192 default_value='multicolumn').tag(config=True)
193 193
194 194 highlight_matching_brackets = Bool(True,
195 195 help="Highlight matching brackets.",
196 196 ).tag(config=True)
197 197
198 198 extra_open_editor_shortcuts = Bool(False,
199 199 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
200 200 "This is in addition to the F2 binding, which is always enabled."
201 201 ).tag(config=True)
202 202
203 203 @observe('term_title')
204 204 def init_term_title(self, change=None):
205 205 # Enable or disable the terminal title.
206 206 if self.term_title:
207 207 toggle_set_term_title(True)
208 208 set_term_title('IPython: ' + abbrev_cwd())
209 209 else:
210 210 toggle_set_term_title(False)
211 211
212 212 def init_display_formatter(self):
213 213 super(TerminalInteractiveShell, self).init_display_formatter()
214 214 # terminal only supports plain text
215 215 self.display_formatter.active_types = ['text/plain']
216 216 # disable `_ipython_display_`
217 217 self.display_formatter.ipython_display_formatter.enabled = False
218 218
219 219 def init_prompt_toolkit_cli(self):
220 220 if self.simple_prompt:
221 221 # Fall back to plain non-interactive output for tests.
222 222 # This is very limited, and only accepts a single line.
223 223 def prompt():
224 224 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
225 225 self.prompt_for_code = prompt
226 226 return
227 227
228 228 # Set up keyboard shortcuts
229 229 kbmanager = KeyBindingManager.for_prompt(
230 230 enable_open_in_editor=self.extra_open_editor_shortcuts,
231 231 )
232 232 register_ipython_shortcuts(kbmanager.registry, self)
233 233
234 234 # Pre-populate history from IPython's history database
235 235 history = InMemoryHistory()
236 236 last_cell = u""
237 237 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
238 238 include_latest=True):
239 239 # Ignore blank lines and consecutive duplicates
240 240 cell = cell.rstrip()
241 241 if cell and (cell != last_cell):
242 history.append(cast_unicode_py2(cell))
242 history.append(cell)
243 243 last_cell = cell
244 244
245 245 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
246 246 style = DynamicStyle(lambda: self._style)
247 247
248 248 editing_mode = getattr(EditingMode, self.editing_mode.upper())
249 249
250 250 def patch_stdout(**kwargs):
251 251 return self.pt_cli.patch_stdout_context(**kwargs)
252 252
253 253 self._pt_app = create_prompt_application(
254 254 editing_mode=editing_mode,
255 255 key_bindings_registry=kbmanager.registry,
256 256 history=history,
257 257 completer=IPythonPTCompleter(shell=self,
258 258 patch_stdout=patch_stdout),
259 259 enable_history_search=True,
260 260 style=style,
261 261 mouse_support=self.mouse_support,
262 262 **self._layout_options()
263 263 )
264 264 self._eventloop = create_eventloop(self.inputhook)
265 265 self.pt_cli = CommandLineInterface(
266 266 self._pt_app, eventloop=self._eventloop,
267 267 output=create_output(true_color=self.true_color))
268 268
269 269 def _make_style_from_name_or_cls(self, name_or_cls):
270 270 """
271 271 Small wrapper that make an IPython compatible style from a style name
272 272
273 273 We need that to add style for prompt ... etc.
274 274 """
275 275 style_overrides = {}
276 276 if name_or_cls == 'legacy':
277 277 legacy = self.colors.lower()
278 278 if legacy == 'linux':
279 279 style_cls = get_style_by_name('monokai')
280 280 style_overrides = _style_overrides_linux
281 281 elif legacy == 'lightbg':
282 282 style_overrides = _style_overrides_light_bg
283 283 style_cls = get_style_by_name('pastie')
284 284 elif legacy == 'neutral':
285 285 # The default theme needs to be visible on both a dark background
286 286 # and a light background, because we can't tell what the terminal
287 287 # looks like. These tweaks to the default theme help with that.
288 288 style_cls = get_style_by_name('default')
289 289 style_overrides.update({
290 290 Token.Number: '#007700',
291 291 Token.Operator: 'noinherit',
292 292 Token.String: '#BB6622',
293 293 Token.Name.Function: '#2080D0',
294 294 Token.Name.Class: 'bold #2080D0',
295 295 Token.Name.Namespace: 'bold #2080D0',
296 296 Token.Prompt: '#009900',
297 297 Token.PromptNum: '#00ff00 bold',
298 298 Token.OutPrompt: '#990000',
299 299 Token.OutPromptNum: '#ff0000 bold',
300 300 })
301 301
302 302 # Hack: Due to limited color support on the Windows console
303 303 # the prompt colors will be wrong without this
304 304 if os.name == 'nt':
305 305 style_overrides.update({
306 306 Token.Prompt: '#ansidarkgreen',
307 307 Token.PromptNum: '#ansigreen bold',
308 308 Token.OutPrompt: '#ansidarkred',
309 309 Token.OutPromptNum: '#ansired bold',
310 310 })
311 311 elif legacy =='nocolor':
312 312 style_cls=_NoStyle
313 313 style_overrides = {}
314 314 else :
315 315 raise ValueError('Got unknown colors: ', legacy)
316 316 else :
317 317 if isinstance(name_or_cls, string_types):
318 318 style_cls = get_style_by_name(name_or_cls)
319 319 else:
320 320 style_cls = name_or_cls
321 321 style_overrides = {
322 322 Token.Prompt: '#009900',
323 323 Token.PromptNum: '#00ff00 bold',
324 324 Token.OutPrompt: '#990000',
325 325 Token.OutPromptNum: '#ff0000 bold',
326 326 }
327 327 style_overrides.update(self.highlighting_style_overrides)
328 328 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
329 329 style_dict=style_overrides)
330 330
331 331 return style
332 332
333 333 def _layout_options(self):
334 334 """
335 335 Return the current layout option for the current Terminal InteractiveShell
336 336 """
337 337 return {
338 338 'lexer':IPythonPTLexer(),
339 339 'reserve_space_for_menu':self.space_for_menu,
340 340 'get_prompt_tokens':self.prompts.in_prompt_tokens,
341 341 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
342 342 'multiline':True,
343 343 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
344 344
345 345 # Highlight matching brackets, but only when this setting is
346 346 # enabled, and only when the DEFAULT_BUFFER has the focus.
347 347 'extra_input_processors': [ConditionalProcessor(
348 348 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
349 349 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
350 350 Condition(lambda cli: self.highlight_matching_brackets))],
351 351 }
352 352
353 353 def _update_layout(self):
354 354 """
355 355 Ask for a re computation of the application layout, if for example ,
356 356 some configuration options have changed.
357 357 """
358 358 if self._pt_app:
359 359 self._pt_app.layout = create_prompt_layout(**self._layout_options())
360 360
361 361 def prompt_for_code(self):
362 362 document = self.pt_cli.run(
363 363 pre_run=self.pre_prompt, reset_current_buffer=True)
364 364 return document.text
365 365
366 366 def enable_win_unicode_console(self):
367 367 if sys.version_info >= (3, 6):
368 368 # Since PEP 528, Python uses the unicode APIs for the Windows
369 369 # console by default, so WUC shouldn't be needed.
370 370 return
371 371
372 372 import win_unicode_console
373 373
374 374 if PY3:
375 375 win_unicode_console.enable()
376 376 else:
377 377 # https://github.com/ipython/ipython/issues/9768
378 378 from win_unicode_console.streams import (TextStreamWrapper,
379 379 stdout_text_transcoded, stderr_text_transcoded)
380 380
381 381 class LenientStrStreamWrapper(TextStreamWrapper):
382 382 def write(self, s):
383 383 if isinstance(s, bytes):
384 384 s = s.decode(self.encoding, 'replace')
385 385
386 386 self.base.write(s)
387 387
388 388 stdout_text_str = LenientStrStreamWrapper(stdout_text_transcoded)
389 389 stderr_text_str = LenientStrStreamWrapper(stderr_text_transcoded)
390 390
391 391 win_unicode_console.enable(stdout=stdout_text_str,
392 392 stderr=stderr_text_str)
393 393
394 394 def init_io(self):
395 395 if sys.platform not in {'win32', 'cli'}:
396 396 return
397 397
398 398 self.enable_win_unicode_console()
399 399
400 400 import colorama
401 401 colorama.init()
402 402
403 403 # For some reason we make these wrappers around stdout/stderr.
404 404 # For now, we need to reset them so all output gets coloured.
405 405 # https://github.com/ipython/ipython/issues/8669
406 406 # io.std* are deprecated, but don't show our own deprecation warnings
407 407 # during initialization of the deprecated API.
408 408 with warnings.catch_warnings():
409 409 warnings.simplefilter('ignore', DeprecationWarning)
410 410 io.stdout = io.IOStream(sys.stdout)
411 411 io.stderr = io.IOStream(sys.stderr)
412 412
413 413 def init_magics(self):
414 414 super(TerminalInteractiveShell, self).init_magics()
415 415 self.register_magics(TerminalMagics)
416 416
417 417 def init_alias(self):
418 418 # The parent class defines aliases that can be safely used with any
419 419 # frontend.
420 420 super(TerminalInteractiveShell, self).init_alias()
421 421
422 422 # Now define aliases that only make sense on the terminal, because they
423 423 # need direct access to the console in a way that we can't emulate in
424 424 # GUI or web frontend
425 425 if os.name == 'posix':
426 426 for cmd in ['clear', 'more', 'less', 'man']:
427 427 self.alias_manager.soft_define_alias(cmd, cmd)
428 428
429 429
430 430 def __init__(self, *args, **kwargs):
431 431 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
432 432 self.init_prompt_toolkit_cli()
433 433 self.init_term_title()
434 434 self.keep_running = True
435 435
436 436 self.debugger_history = InMemoryHistory()
437 437
438 438 def ask_exit(self):
439 439 self.keep_running = False
440 440
441 441 rl_next_input = None
442 442
443 443 def pre_prompt(self):
444 444 if self.rl_next_input:
445 445 # We can't set the buffer here, because it will be reset just after
446 446 # this. Adding a callable to pre_run_callables does what we need
447 447 # after the buffer is reset.
448 448 s = cast_unicode_py2(self.rl_next_input)
449 449 def set_doc():
450 450 self.pt_cli.application.buffer.document = Document(s)
451 451 if hasattr(self.pt_cli, 'pre_run_callables'):
452 452 self.pt_cli.pre_run_callables.append(set_doc)
453 453 else:
454 454 # Older version of prompt_toolkit; it's OK to set the document
455 455 # directly here.
456 456 set_doc()
457 457 self.rl_next_input = None
458 458
459 459 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
460 460
461 461 if display_banner is not DISPLAY_BANNER_DEPRECATED:
462 462 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
463 463
464 464 self.keep_running = True
465 465 while self.keep_running:
466 466 print(self.separate_in, end='')
467 467
468 468 try:
469 469 code = self.prompt_for_code()
470 470 except EOFError:
471 471 if (not self.confirm_exit) \
472 472 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
473 473 self.ask_exit()
474 474
475 475 else:
476 476 if code:
477 477 self.run_cell(code, store_history=True)
478 478
479 479 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
480 480 # An extra layer of protection in case someone mashing Ctrl-C breaks
481 481 # out of our internal code.
482 482 if display_banner is not DISPLAY_BANNER_DEPRECATED:
483 483 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
484 484 while True:
485 485 try:
486 486 self.interact()
487 487 break
488 488 except KeyboardInterrupt as e:
489 489 print("\n%s escaped interact()\n" % type(e).__name__)
490 490 finally:
491 491 # An interrupt during the eventloop will mess up the
492 492 # internal state of the prompt_toolkit library.
493 493 # Stopping the eventloop fixes this, see
494 494 # https://github.com/ipython/ipython/pull/9867
495 495 if hasattr(self, '_eventloop'):
496 496 self._eventloop.stop()
497 497
498 498 _inputhook = None
499 499 def inputhook(self, context):
500 500 if self._inputhook is not None:
501 501 self._inputhook(context)
502 502
503 503 active_eventloop = None
504 504 def enable_gui(self, gui=None):
505 505 if gui:
506 506 self.active_eventloop, self._inputhook =\
507 507 get_inputhook_name_and_func(gui)
508 508 else:
509 509 self.active_eventloop = self._inputhook = None
510 510
511 511 # Run !system commands directly, not through pipes, so terminal programs
512 512 # work correctly.
513 513 system = InteractiveShell.system_raw
514 514
515 515 def auto_rewrite_input(self, cmd):
516 516 """Overridden from the parent class to use fancy rewriting prompt"""
517 517 if not self.show_rewritten_input:
518 518 return
519 519
520 520 tokens = self.prompts.rewrite_prompt_tokens()
521 521 if self.pt_cli:
522 522 self.pt_cli.print_tokens(tokens)
523 523 print(cmd)
524 524 else:
525 525 prompt = ''.join(s for t, s in tokens)
526 526 print(prompt, cmd, sep='')
527 527
528 528 _prompts_before = None
529 529 def switch_doctest_mode(self, mode):
530 530 """Switch prompts to classic for %doctest_mode"""
531 531 if mode:
532 532 self._prompts_before = self.prompts
533 533 self.prompts = ClassicPrompts(self)
534 534 elif self._prompts_before:
535 535 self.prompts = self._prompts_before
536 536 self._prompts_before = None
537 537 self._update_layout()
538 538
539 539
540 540 InteractiveShellABC.register(TerminalInteractiveShell)
541 541
542 542 if __name__ == '__main__':
543 543 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now