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