##// END OF EJS Templates
disable save-thread when using in-memory history db.
MinRK -
Show More
@@ -1,976 +1,977 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 18 from io import open as io_open
19 19 import os
20 20 import re
21 21 try:
22 22 import sqlite3
23 23 except ImportError:
24 24 sqlite3 = None
25 25 import threading
26 26
27 27 # Our own packages
28 28 from IPython.core.error import StdinNotImplementedError
29 29 from IPython.config.configurable import Configurable
30 30 from IPython.external.decorator import decorator
31 31 from IPython.testing.skipdoctest import skip_doctest
32 32 from IPython.utils import io
33 33 from IPython.utils.path import locate_profile
34 34 from IPython.utils.traitlets import Bool, Dict, Instance, Integer, List, Unicode
35 35 from IPython.utils.warn import warn
36 36
37 37 #-----------------------------------------------------------------------------
38 38 # Classes and functions
39 39 #-----------------------------------------------------------------------------
40 40
41 41 class DummyDB(object):
42 42 """Dummy DB that will act as a black hole for history.
43 43
44 44 Only used in the absence of sqlite"""
45 45 def execute(*args, **kwargs):
46 46 return []
47 47
48 48 def commit(self, *args, **kwargs):
49 49 pass
50 50
51 51 def __enter__(self, *args, **kwargs):
52 52 pass
53 53
54 54 def __exit__(self, *args, **kwargs):
55 55 pass
56 56
57 57 @decorator
58 58 def needs_sqlite(f,*a,**kw):
59 59 """return an empty list in the absence of sqlite"""
60 60 if sqlite3 is None:
61 61 return []
62 62 else:
63 63 return f(*a,**kw)
64 64
65 65 class HistoryAccessor(Configurable):
66 66 """Access the history database without adding to it.
67 67
68 68 This is intended for use by standalone history tools. IPython shells use
69 69 HistoryManager, below, which is a subclass of this."""
70 70
71 71 # String holding the path to the history file
72 72 hist_file = Unicode(config=True,
73 73 help="""Path to file to use for SQLite history database.
74 74
75 75 By default, IPython will put the history database in the IPython profile
76 76 directory. If you would rather share one history among profiles,
77 77 you ca set this value in each, so that they are consistent.
78 78
79 79 Due to an issue with fcntl, SQLite is known to misbehave on some NFS mounts.
80 80 If you see IPython hanging, try setting this to something on a local disk,
81 81 e.g::
82 82
83 83 ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
84 84
85 85 """)
86 86
87 87
88 88 # The SQLite database
89 89 if sqlite3:
90 90 db = Instance(sqlite3.Connection)
91 91 else:
92 92 db = Instance(DummyDB)
93 93
94 94 def __init__(self, profile='default', hist_file=u'', config=None, **traits):
95 95 """Create a new history accessor.
96 96
97 97 Parameters
98 98 ----------
99 99 profile : str
100 100 The name of the profile from which to open history.
101 101 hist_file : str
102 102 Path to an SQLite history database stored by IPython. If specified,
103 103 hist_file overrides profile.
104 104 config :
105 105 Config object. hist_file can also be set through this.
106 106 """
107 107 # We need a pointer back to the shell for various tasks.
108 108 super(HistoryAccessor, self).__init__(config=config, **traits)
109 109 # defer setting hist_file from kwarg until after init,
110 110 # otherwise the default kwarg value would clobber any value
111 111 # set by config
112 112 if hist_file:
113 113 self.hist_file = hist_file
114 114
115 115 if self.hist_file == u'':
116 116 # No one has set the hist_file, yet.
117 117 self.hist_file = self._get_hist_file_name(profile)
118 118
119 119 if sqlite3 is None:
120 120 warn("IPython History requires SQLite, your history will not be saved\n")
121 121 self.db = DummyDB()
122 122 return
123 123
124 124 try:
125 125 self.init_db()
126 126 except sqlite3.DatabaseError:
127 127 if os.path.isfile(self.hist_file):
128 128 # Try to move the file out of the way
129 129 base,ext = os.path.splitext(self.hist_file)
130 130 newpath = base + '-corrupt' + ext
131 131 os.rename(self.hist_file, newpath)
132 132 print("ERROR! History file wasn't a valid SQLite database.",
133 133 "It was moved to %s" % newpath, "and a new file created.")
134 134 self.init_db()
135 135 else:
136 136 # The hist_file is probably :memory: or something else.
137 137 raise
138 138
139 139 def _get_hist_file_name(self, profile='default'):
140 140 """Find the history file for the given profile name.
141 141
142 142 This is overridden by the HistoryManager subclass, to use the shell's
143 143 active profile.
144 144
145 145 Parameters
146 146 ----------
147 147 profile : str
148 148 The name of a profile which has a history file.
149 149 """
150 150 return os.path.join(locate_profile(profile), 'history.sqlite')
151 151
152 152 def init_db(self):
153 153 """Connect to the database, and create tables if necessary."""
154 154 # use detect_types so that timestamps return datetime objects
155 155 self.db = sqlite3.connect(self.hist_file, detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
156 156 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
157 157 primary key autoincrement, start timestamp,
158 158 end timestamp, num_cmds integer, remark text)""")
159 159 self.db.execute("""CREATE TABLE IF NOT EXISTS history
160 160 (session integer, line integer, source text, source_raw text,
161 161 PRIMARY KEY (session, line))""")
162 162 # Output history is optional, but ensure the table's there so it can be
163 163 # enabled later.
164 164 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
165 165 (session integer, line integer, output text,
166 166 PRIMARY KEY (session, line))""")
167 167 self.db.commit()
168 168
169 169 def writeout_cache(self):
170 170 """Overridden by HistoryManager to dump the cache before certain
171 171 database lookups."""
172 172 pass
173 173
174 174 ## -------------------------------
175 175 ## Methods for retrieving history:
176 176 ## -------------------------------
177 177 def _run_sql(self, sql, params, raw=True, output=False):
178 178 """Prepares and runs an SQL query for the history database.
179 179
180 180 Parameters
181 181 ----------
182 182 sql : str
183 183 Any filtering expressions to go after SELECT ... FROM ...
184 184 params : tuple
185 185 Parameters passed to the SQL query (to replace "?")
186 186 raw, output : bool
187 187 See :meth:`get_range`
188 188
189 189 Returns
190 190 -------
191 191 Tuples as :meth:`get_range`
192 192 """
193 193 toget = 'source_raw' if raw else 'source'
194 194 sqlfrom = "history"
195 195 if output:
196 196 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
197 197 toget = "history.%s, output_history.output" % toget
198 198 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
199 199 (toget, sqlfrom) + sql, params)
200 200 if output: # Regroup into 3-tuples, and parse JSON
201 201 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
202 202 return cur
203 203
204 204 @needs_sqlite
205 205 def get_session_info(self, session=0):
206 206 """get info about a session
207 207
208 208 Parameters
209 209 ----------
210 210
211 211 session : int
212 212 Session number to retrieve. The current session is 0, and negative
213 213 numbers count back from current session, so -1 is previous session.
214 214
215 215 Returns
216 216 -------
217 217
218 218 (session_id [int], start [datetime], end [datetime], num_cmds [int], remark [unicode])
219 219
220 220 Sessions that are running or did not exit cleanly will have `end=None`
221 221 and `num_cmds=None`.
222 222
223 223 """
224 224
225 225 if session <= 0:
226 226 session += self.session_number
227 227
228 228 query = "SELECT * from sessions where session == ?"
229 229 return self.db.execute(query, (session,)).fetchone()
230 230
231 231 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
232 232 """Get the last n lines from the history database.
233 233
234 234 Parameters
235 235 ----------
236 236 n : int
237 237 The number of lines to get
238 238 raw, output : bool
239 239 See :meth:`get_range`
240 240 include_latest : bool
241 241 If False (default), n+1 lines are fetched, and the latest one
242 242 is discarded. This is intended to be used where the function
243 243 is called by a user command, which it should not return.
244 244
245 245 Returns
246 246 -------
247 247 Tuples as :meth:`get_range`
248 248 """
249 249 self.writeout_cache()
250 250 if not include_latest:
251 251 n += 1
252 252 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
253 253 (n,), raw=raw, output=output)
254 254 if not include_latest:
255 255 return reversed(list(cur)[1:])
256 256 return reversed(list(cur))
257 257
258 258 def search(self, pattern="*", raw=True, search_raw=True,
259 259 output=False):
260 260 """Search the database using unix glob-style matching (wildcards
261 261 * and ?).
262 262
263 263 Parameters
264 264 ----------
265 265 pattern : str
266 266 The wildcarded pattern to match when searching
267 267 search_raw : bool
268 268 If True, search the raw input, otherwise, the parsed input
269 269 raw, output : bool
270 270 See :meth:`get_range`
271 271
272 272 Returns
273 273 -------
274 274 Tuples as :meth:`get_range`
275 275 """
276 276 tosearch = "source_raw" if search_raw else "source"
277 277 if output:
278 278 tosearch = "history." + tosearch
279 279 self.writeout_cache()
280 280 return self._run_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
281 281 raw=raw, output=output)
282 282
283 283 def get_range(self, session, start=1, stop=None, raw=True,output=False):
284 284 """Retrieve input by session.
285 285
286 286 Parameters
287 287 ----------
288 288 session : int
289 289 Session number to retrieve.
290 290 start : int
291 291 First line to retrieve.
292 292 stop : int
293 293 End of line range (excluded from output itself). If None, retrieve
294 294 to the end of the session.
295 295 raw : bool
296 296 If True, return untranslated input
297 297 output : bool
298 298 If True, attempt to include output. This will be 'real' Python
299 299 objects for the current session, or text reprs from previous
300 300 sessions if db_log_output was enabled at the time. Where no output
301 301 is found, None is used.
302 302
303 303 Returns
304 304 -------
305 305 An iterator over the desired lines. Each line is a 3-tuple, either
306 306 (session, line, input) if output is False, or
307 307 (session, line, (input, output)) if output is True.
308 308 """
309 309 if stop:
310 310 lineclause = "line >= ? AND line < ?"
311 311 params = (session, start, stop)
312 312 else:
313 313 lineclause = "line>=?"
314 314 params = (session, start)
315 315
316 316 return self._run_sql("WHERE session==? AND %s""" % lineclause,
317 317 params, raw=raw, output=output)
318 318
319 319 def get_range_by_str(self, rangestr, raw=True, output=False):
320 320 """Get lines of history from a string of ranges, as used by magic
321 321 commands %hist, %save, %macro, etc.
322 322
323 323 Parameters
324 324 ----------
325 325 rangestr : str
326 326 A string specifying ranges, e.g. "5 ~2/1-4". See
327 327 :func:`magic_history` for full details.
328 328 raw, output : bool
329 329 As :meth:`get_range`
330 330
331 331 Returns
332 332 -------
333 333 Tuples as :meth:`get_range`
334 334 """
335 335 for sess, s, e in extract_hist_ranges(rangestr):
336 336 for line in self.get_range(sess, s, e, raw=raw, output=output):
337 337 yield line
338 338
339 339
340 340 class HistoryManager(HistoryAccessor):
341 341 """A class to organize all history-related functionality in one place.
342 342 """
343 343 # Public interface
344 344
345 345 # An instance of the IPython shell we are attached to
346 346 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
347 347 # Lists to hold processed and raw history. These start with a blank entry
348 348 # so that we can index them starting from 1
349 349 input_hist_parsed = List([""])
350 350 input_hist_raw = List([""])
351 351 # A list of directories visited during session
352 352 dir_hist = List()
353 353 def _dir_hist_default(self):
354 354 try:
355 355 return [os.getcwdu()]
356 356 except OSError:
357 357 return []
358 358
359 359 # A dict of output history, keyed with ints from the shell's
360 360 # execution count.
361 361 output_hist = Dict()
362 362 # The text/plain repr of outputs.
363 363 output_hist_reprs = Dict()
364 364
365 365 # The number of the current session in the history database
366 366 session_number = Integer()
367 367 # Should we log output to the database? (default no)
368 368 db_log_output = Bool(False, config=True)
369 369 # Write to database every x commands (higher values save disk access & power)
370 370 # Values of 1 or less effectively disable caching.
371 371 db_cache_size = Integer(0, config=True)
372 372 # The input and output caches
373 373 db_input_cache = List()
374 374 db_output_cache = List()
375 375
376 376 # History saving in separate thread
377 377 save_thread = Instance('IPython.core.history.HistorySavingThread')
378 378 try: # Event is a function returning an instance of _Event...
379 379 save_flag = Instance(threading._Event)
380 380 except AttributeError: # ...until Python 3.3, when it's a class.
381 381 save_flag = Instance(threading.Event)
382 382
383 383 # Private interface
384 384 # Variables used to store the three last inputs from the user. On each new
385 385 # history update, we populate the user's namespace with these, shifted as
386 386 # necessary.
387 387 _i00 = Unicode(u'')
388 388 _i = Unicode(u'')
389 389 _ii = Unicode(u'')
390 390 _iii = Unicode(u'')
391 391
392 392 # A regex matching all forms of the exit command, so that we don't store
393 393 # them in the history (it's annoying to rewind the first entry and land on
394 394 # an exit call).
395 395 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
396 396
397 397 def __init__(self, shell=None, config=None, **traits):
398 398 """Create a new history manager associated with a shell instance.
399 399 """
400 400 # We need a pointer back to the shell for various tasks.
401 401 super(HistoryManager, self).__init__(shell=shell, config=config,
402 402 **traits)
403 403 self.save_flag = threading.Event()
404 404 self.db_input_cache_lock = threading.Lock()
405 405 self.db_output_cache_lock = threading.Lock()
406 self.save_thread = HistorySavingThread(self)
407 self.save_thread.start()
406 if self.hist_file != ':memory:':
407 self.save_thread = HistorySavingThread(self)
408 self.save_thread.start()
408 409
409 410 self.new_session()
410 411
411 412 def _get_hist_file_name(self, profile=None):
412 413 """Get default history file name based on the Shell's profile.
413 414
414 415 The profile parameter is ignored, but must exist for compatibility with
415 416 the parent class."""
416 417 profile_dir = self.shell.profile_dir.location
417 418 return os.path.join(profile_dir, 'history.sqlite')
418 419
419 420 @needs_sqlite
420 421 def new_session(self, conn=None):
421 422 """Get a new session number."""
422 423 if conn is None:
423 424 conn = self.db
424 425
425 426 with conn:
426 427 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
427 428 NULL, "") """, (datetime.datetime.now(),))
428 429 self.session_number = cur.lastrowid
429 430
430 431 def end_session(self):
431 432 """Close the database session, filling in the end time and line count."""
432 433 self.writeout_cache()
433 434 with self.db:
434 435 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
435 436 session==?""", (datetime.datetime.now(),
436 437 len(self.input_hist_parsed)-1, self.session_number))
437 438 self.session_number = 0
438 439
439 440 def name_session(self, name):
440 441 """Give the current session a name in the history database."""
441 442 with self.db:
442 443 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
443 444 (name, self.session_number))
444 445
445 446 def reset(self, new_session=True):
446 447 """Clear the session history, releasing all object references, and
447 448 optionally open a new session."""
448 449 self.output_hist.clear()
449 450 # The directory history can't be completely empty
450 451 self.dir_hist[:] = [os.getcwdu()]
451 452
452 453 if new_session:
453 454 if self.session_number:
454 455 self.end_session()
455 456 self.input_hist_parsed[:] = [""]
456 457 self.input_hist_raw[:] = [""]
457 458 self.new_session()
458 459
459 460 # ------------------------------
460 461 # Methods for retrieving history
461 462 # ------------------------------
462 463 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
463 464 """Get input and output history from the current session. Called by
464 465 get_range, and takes similar parameters."""
465 466 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
466 467
467 468 n = len(input_hist)
468 469 if start < 0:
469 470 start += n
470 471 if not stop or (stop > n):
471 472 stop = n
472 473 elif stop < 0:
473 474 stop += n
474 475
475 476 for i in range(start, stop):
476 477 if output:
477 478 line = (input_hist[i], self.output_hist_reprs.get(i))
478 479 else:
479 480 line = input_hist[i]
480 481 yield (0, i, line)
481 482
482 483 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
483 484 """Retrieve input by session.
484 485
485 486 Parameters
486 487 ----------
487 488 session : int
488 489 Session number to retrieve. The current session is 0, and negative
489 490 numbers count back from current session, so -1 is previous session.
490 491 start : int
491 492 First line to retrieve.
492 493 stop : int
493 494 End of line range (excluded from output itself). If None, retrieve
494 495 to the end of the session.
495 496 raw : bool
496 497 If True, return untranslated input
497 498 output : bool
498 499 If True, attempt to include output. This will be 'real' Python
499 500 objects for the current session, or text reprs from previous
500 501 sessions if db_log_output was enabled at the time. Where no output
501 502 is found, None is used.
502 503
503 504 Returns
504 505 -------
505 506 An iterator over the desired lines. Each line is a 3-tuple, either
506 507 (session, line, input) if output is False, or
507 508 (session, line, (input, output)) if output is True.
508 509 """
509 510 if session <= 0:
510 511 session += self.session_number
511 512 if session==self.session_number: # Current session
512 513 return self._get_range_session(start, stop, raw, output)
513 514 return super(HistoryManager, self).get_range(session, start, stop, raw, output)
514 515
515 516 ## ----------------------------
516 517 ## Methods for storing history:
517 518 ## ----------------------------
518 519 def store_inputs(self, line_num, source, source_raw=None):
519 520 """Store source and raw input in history and create input cache
520 521 variables _i*.
521 522
522 523 Parameters
523 524 ----------
524 525 line_num : int
525 526 The prompt number of this input.
526 527
527 528 source : str
528 529 Python input.
529 530
530 531 source_raw : str, optional
531 532 If given, this is the raw input without any IPython transformations
532 533 applied to it. If not given, ``source`` is used.
533 534 """
534 535 if source_raw is None:
535 536 source_raw = source
536 537 source = source.rstrip('\n')
537 538 source_raw = source_raw.rstrip('\n')
538 539
539 540 # do not store exit/quit commands
540 541 if self._exit_re.match(source_raw.strip()):
541 542 return
542 543
543 544 self.input_hist_parsed.append(source)
544 545 self.input_hist_raw.append(source_raw)
545 546
546 547 with self.db_input_cache_lock:
547 548 self.db_input_cache.append((line_num, source, source_raw))
548 549 # Trigger to flush cache and write to DB.
549 550 if len(self.db_input_cache) >= self.db_cache_size:
550 551 self.save_flag.set()
551 552
552 553 # update the auto _i variables
553 554 self._iii = self._ii
554 555 self._ii = self._i
555 556 self._i = self._i00
556 557 self._i00 = source_raw
557 558
558 559 # hackish access to user namespace to create _i1,_i2... dynamically
559 560 new_i = '_i%s' % line_num
560 561 to_main = {'_i': self._i,
561 562 '_ii': self._ii,
562 563 '_iii': self._iii,
563 564 new_i : self._i00 }
564 565
565 566 self.shell.push(to_main, interactive=False)
566 567
567 568 def store_output(self, line_num):
568 569 """If database output logging is enabled, this saves all the
569 570 outputs from the indicated prompt number to the database. It's
570 571 called by run_cell after code has been executed.
571 572
572 573 Parameters
573 574 ----------
574 575 line_num : int
575 576 The line number from which to save outputs
576 577 """
577 578 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
578 579 return
579 580 output = self.output_hist_reprs[line_num]
580 581
581 582 with self.db_output_cache_lock:
582 583 self.db_output_cache.append((line_num, output))
583 584 if self.db_cache_size <= 1:
584 585 self.save_flag.set()
585 586
586 587 def _writeout_input_cache(self, conn):
587 588 with conn:
588 589 for line in self.db_input_cache:
589 590 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
590 591 (self.session_number,)+line)
591 592
592 593 def _writeout_output_cache(self, conn):
593 594 with conn:
594 595 for line in self.db_output_cache:
595 596 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
596 597 (self.session_number,)+line)
597 598
598 599 @needs_sqlite
599 600 def writeout_cache(self, conn=None):
600 601 """Write any entries in the cache to the database."""
601 602 if conn is None:
602 603 conn = self.db
603 604
604 605 with self.db_input_cache_lock:
605 606 try:
606 607 self._writeout_input_cache(conn)
607 608 except sqlite3.IntegrityError:
608 609 self.new_session(conn)
609 610 print("ERROR! Session/line number was not unique in",
610 611 "database. History logging moved to new session",
611 612 self.session_number)
612 613 try: # Try writing to the new session. If this fails, don't recurse
613 614 self._writeout_input_cache(conn)
614 615 except sqlite3.IntegrityError:
615 616 pass
616 617 finally:
617 618 self.db_input_cache = []
618 619
619 620 with self.db_output_cache_lock:
620 621 try:
621 622 self._writeout_output_cache(conn)
622 623 except sqlite3.IntegrityError:
623 624 print("!! Session/line number for output was not unique",
624 625 "in database. Output will not be stored.")
625 626 finally:
626 627 self.db_output_cache = []
627 628
628 629
629 630 class HistorySavingThread(threading.Thread):
630 631 """This thread takes care of writing history to the database, so that
631 632 the UI isn't held up while that happens.
632 633
633 634 It waits for the HistoryManager's save_flag to be set, then writes out
634 635 the history cache. The main thread is responsible for setting the flag when
635 636 the cache size reaches a defined threshold."""
636 637 daemon = True
637 638 stop_now = False
638 639 def __init__(self, history_manager):
639 640 super(HistorySavingThread, self).__init__()
640 641 self.history_manager = history_manager
641 642 atexit.register(self.stop)
642 643
643 644 @needs_sqlite
644 645 def run(self):
645 646 # We need a separate db connection per thread:
646 647 try:
647 648 self.db = sqlite3.connect(self.history_manager.hist_file)
648 649 while True:
649 650 self.history_manager.save_flag.wait()
650 651 if self.stop_now:
651 652 return
652 653 self.history_manager.save_flag.clear()
653 654 self.history_manager.writeout_cache(self.db)
654 655 except Exception as e:
655 656 print(("The history saving thread hit an unexpected error (%s)."
656 657 "History will not be written to the database.") % repr(e))
657 658
658 659 def stop(self):
659 660 """This can be called from the main thread to safely stop this thread.
660 661
661 662 Note that it does not attempt to write out remaining history before
662 663 exiting. That should be done by calling the HistoryManager's
663 664 end_session method."""
664 665 self.stop_now = True
665 666 self.history_manager.save_flag.set()
666 667 self.join()
667 668
668 669
669 670 # To match, e.g. ~5/8-~2/3
670 671 range_re = re.compile(r"""
671 672 ((?P<startsess>~?\d+)/)?
672 673 (?P<start>\d+) # Only the start line num is compulsory
673 674 ((?P<sep>[\-:])
674 675 ((?P<endsess>~?\d+)/)?
675 676 (?P<end>\d+))?
676 677 $""", re.VERBOSE)
677 678
678 679 def extract_hist_ranges(ranges_str):
679 680 """Turn a string of history ranges into 3-tuples of (session, start, stop).
680 681
681 682 Examples
682 683 --------
683 684 list(extract_input_ranges("~8/5-~7/4 2"))
684 685 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
685 686 """
686 687 for range_str in ranges_str.split():
687 688 rmatch = range_re.match(range_str)
688 689 if not rmatch:
689 690 continue
690 691 start = int(rmatch.group("start"))
691 692 end = rmatch.group("end")
692 693 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
693 694 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
694 695 end += 1
695 696 startsess = rmatch.group("startsess") or "0"
696 697 endsess = rmatch.group("endsess") or startsess
697 698 startsess = int(startsess.replace("~","-"))
698 699 endsess = int(endsess.replace("~","-"))
699 700 assert endsess >= startsess
700 701
701 702 if endsess == startsess:
702 703 yield (startsess, start, end)
703 704 continue
704 705 # Multiple sessions in one range:
705 706 yield (startsess, start, None)
706 707 for sess in range(startsess+1, endsess):
707 708 yield (sess, 1, None)
708 709 yield (endsess, 1, end)
709 710
710 711 def _format_lineno(session, line):
711 712 """Helper function to format line numbers properly."""
712 713 if session == 0:
713 714 return str(line)
714 715 return "%s#%s" % (session, line)
715 716
716 717 @skip_doctest
717 718 def magic_history(self, parameter_s = ''):
718 719 """Print input history (_i<n> variables), with most recent last.
719 720
720 721 %history [-o -p -t -n] [-f filename] [range | -g pattern | -l number]
721 722
722 723 By default, input history is printed without line numbers so it can be
723 724 directly pasted into an editor. Use -n to show them.
724 725
725 726 By default, all input history from the current session is displayed.
726 727 Ranges of history can be indicated using the syntax:
727 728 4 : Line 4, current session
728 729 4-6 : Lines 4-6, current session
729 730 243/1-5: Lines 1-5, session 243
730 731 ~2/7 : Line 7, session 2 before current
731 732 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
732 733 of 6 sessions ago.
733 734 Multiple ranges can be entered, separated by spaces
734 735
735 736 The same syntax is used by %macro, %save, %edit, %rerun
736 737
737 738 Options:
738 739
739 740 -n: print line numbers for each input.
740 741 This feature is only available if numbered prompts are in use.
741 742
742 743 -o: also print outputs for each input.
743 744
744 745 -p: print classic '>>>' python prompts before each input. This is useful
745 746 for making documentation, and in conjunction with -o, for producing
746 747 doctest-ready output.
747 748
748 749 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
749 750
750 751 -t: print the 'translated' history, as IPython understands it. IPython
751 752 filters your input and converts it all into valid Python source before
752 753 executing it (things like magics or aliases are turned into function
753 754 calls, for example). With this option, you'll see the native history
754 755 instead of the user-entered version: '%cd /' will be seen as
755 756 'get_ipython().magic("%cd /")' instead of '%cd /'.
756 757
757 758 -g: treat the arg as a pattern to grep for in (full) history.
758 759 This includes the saved history (almost all commands ever written).
759 760 Use '%hist -g' to show full saved history (may be very long).
760 761
761 762 -l: get the last n lines from all sessions. Specify n as a single arg, or
762 763 the default is the last 10 lines.
763 764
764 765 -f FILENAME: instead of printing the output to the screen, redirect it to
765 766 the given file. The file is always overwritten, though *when it can*,
766 767 IPython asks for confirmation first. In particular, running the command
767 768 "history -f FILENAME" from the IPython Notebook interface will replace
768 769 FILENAME even if it already exists *without* confirmation.
769 770
770 771 Examples
771 772 --------
772 773 ::
773 774
774 775 In [6]: %hist -n 4-6
775 776 4:a = 12
776 777 5:print a**2
777 778 6:%hist -n 4-6
778 779
779 780 """
780 781
781 782 if not self.shell.displayhook.do_full_cache:
782 783 print('This feature is only available if numbered prompts are in use.')
783 784 return
784 785 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
785 786
786 787 # For brevity
787 788 history_manager = self.shell.history_manager
788 789
789 790 def _format_lineno(session, line):
790 791 """Helper function to format line numbers properly."""
791 792 if session in (0, history_manager.session_number):
792 793 return str(line)
793 794 return "%s/%s" % (session, line)
794 795
795 796 # Check if output to specific file was requested.
796 797 try:
797 798 outfname = opts['f']
798 799 except KeyError:
799 800 outfile = io.stdout # default
800 801 # We don't want to close stdout at the end!
801 802 close_at_end = False
802 803 else:
803 804 if os.path.exists(outfname):
804 805 try:
805 806 ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
806 807 except StdinNotImplementedError:
807 808 ans = True
808 809 if not ans:
809 810 print('Aborting.')
810 811 return
811 812 print("Overwriting file.")
812 813 outfile = io_open(outfname, 'w', encoding='utf-8')
813 814 close_at_end = True
814 815
815 816 print_nums = 'n' in opts
816 817 get_output = 'o' in opts
817 818 pyprompts = 'p' in opts
818 819 # Raw history is the default
819 820 raw = not('t' in opts)
820 821
821 822 default_length = 40
822 823 pattern = None
823 824
824 825 if 'g' in opts: # Glob search
825 826 pattern = "*" + args + "*" if args else "*"
826 827 hist = history_manager.search(pattern, raw=raw, output=get_output)
827 828 print_nums = True
828 829 elif 'l' in opts: # Get 'tail'
829 830 try:
830 831 n = int(args)
831 832 except ValueError, IndexError:
832 833 n = 10
833 834 hist = history_manager.get_tail(n, raw=raw, output=get_output)
834 835 else:
835 836 if args: # Get history by ranges
836 837 hist = history_manager.get_range_by_str(args, raw, get_output)
837 838 else: # Just get history for the current session
838 839 hist = history_manager.get_range(raw=raw, output=get_output)
839 840
840 841 # We could be displaying the entire history, so let's not try to pull it
841 842 # into a list in memory. Anything that needs more space will just misalign.
842 843 width = 4
843 844
844 845 for session, lineno, inline in hist:
845 846 # Print user history with tabs expanded to 4 spaces. The GUI clients
846 847 # use hard tabs for easier usability in auto-indented code, but we want
847 848 # to produce PEP-8 compliant history for safe pasting into an editor.
848 849 if get_output:
849 850 inline, output = inline
850 851 inline = inline.expandtabs(4).rstrip()
851 852
852 853 multiline = "\n" in inline
853 854 line_sep = '\n' if multiline else ' '
854 855 if print_nums:
855 856 print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width),
856 857 line_sep), file=outfile, end=u'')
857 858 if pyprompts:
858 859 print(u">>> ", end=u"", file=outfile)
859 860 if multiline:
860 861 inline = "\n... ".join(inline.splitlines()) + "\n..."
861 862 print(inline, file=outfile)
862 863 if get_output and output:
863 864 print(output, file=outfile)
864 865
865 866 if close_at_end:
866 867 outfile.close()
867 868
868 869
869 870 def magic_rep(self, arg):
870 871 r"""Repeat a command, or get command to input line for editing.
871 872
872 873 %recall and %rep are equivalent.
873 874
874 875 - %recall (no arguments):
875 876
876 877 Place a string version of last computation result (stored in the special '_'
877 878 variable) to the next input prompt. Allows you to create elaborate command
878 879 lines without using copy-paste::
879 880
880 881 In[1]: l = ["hei", "vaan"]
881 882 In[2]: "".join(l)
882 883 Out[2]: heivaan
883 884 In[3]: %rep
884 885 In[4]: heivaan_ <== cursor blinking
885 886
886 887 %recall 45
887 888
888 889 Place history line 45 on the next input prompt. Use %hist to find
889 890 out the number.
890 891
891 892 %recall 1-4
892 893
893 894 Combine the specified lines into one cell, and place it on the next
894 895 input prompt. See %history for the slice syntax.
895 896
896 897 %recall foo+bar
897 898
898 899 If foo+bar can be evaluated in the user namespace, the result is
899 900 placed at the next input prompt. Otherwise, the history is searched
900 901 for lines which contain that substring, and the most recent one is
901 902 placed at the next input prompt.
902 903 """
903 904 if not arg: # Last output
904 905 self.set_next_input(str(self.shell.user_ns["_"]))
905 906 return
906 907 # Get history range
907 908 histlines = self.history_manager.get_range_by_str(arg)
908 909 cmd = "\n".join(x[2] for x in histlines)
909 910 if cmd:
910 911 self.set_next_input(cmd.rstrip())
911 912 return
912 913
913 914 try: # Variable in user namespace
914 915 cmd = str(eval(arg, self.shell.user_ns))
915 916 except Exception: # Search for term in history
916 917 histlines = self.history_manager.search("*"+arg+"*")
917 918 for h in reversed([x[2] for x in histlines]):
918 919 if 'rep' in h:
919 920 continue
920 921 self.set_next_input(h.rstrip())
921 922 return
922 923 else:
923 924 self.set_next_input(cmd.rstrip())
924 925 print("Couldn't evaluate or find in history:", arg)
925 926
926 927 def magic_rerun(self, parameter_s=''):
927 928 """Re-run previous input
928 929
929 930 By default, you can specify ranges of input history to be repeated
930 931 (as with %history). With no arguments, it will repeat the last line.
931 932
932 933 Options:
933 934
934 935 -l <n> : Repeat the last n lines of input, not including the
935 936 current command.
936 937
937 938 -g foo : Repeat the most recent line which contains foo
938 939 """
939 940 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
940 941 if "l" in opts: # Last n lines
941 942 n = int(opts['l'])
942 943 hist = self.history_manager.get_tail(n)
943 944 elif "g" in opts: # Search
944 945 p = "*"+opts['g']+"*"
945 946 hist = list(self.history_manager.search(p))
946 947 for l in reversed(hist):
947 948 if "rerun" not in l[2]:
948 949 hist = [l] # The last match which isn't a %rerun
949 950 break
950 951 else:
951 952 hist = [] # No matches except %rerun
952 953 elif args: # Specify history ranges
953 954 hist = self.history_manager.get_range_by_str(args)
954 955 else: # Last line
955 956 hist = self.history_manager.get_tail(1)
956 957 hist = [x[2] for x in hist]
957 958 if not hist:
958 959 print("No lines in history match specification")
959 960 return
960 961 histlines = "\n".join(hist)
961 962 print("=== Executing: ===")
962 963 print(histlines)
963 964 print("=== Output: ===")
964 965 self.run_cell("\n".join(hist), store_history=False)
965 966
966 967
967 968 def init_ipython(ip):
968 969 ip.define_magic("rep", magic_rep)
969 970 ip.define_magic("recall", magic_rep)
970 971 ip.define_magic("rerun", magic_rerun)
971 972 ip.define_magic("hist",magic_history) # Alternative name
972 973 ip.define_magic("history",magic_history)
973 974
974 975 # XXX - ipy_completers are in quarantine, need to be updated to new apis
975 976 #import ipy_completers
976 977 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
General Comments 0
You need to be logged in to leave comments. Login now