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