##// END OF EJS Templates
Catch errors in history save thread, and print a briefer error message, rather than an ugly traceback.
Thomas Kluyver -
Show More
@@ -1,806 +1,810 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 83 # N.B. Event is a function returning an instance of _Event.
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 474 """This thread takes care of writing history to the database, so that
475 475 the UI isn't held up while that happens.
476 476
477 477 It waits for the HistoryManager's save_flag to be set, then writes out
478 478 the history cache. The main thread is responsible for setting the flag when
479 479 the cache size reaches a defined threshold."""
480 480 daemon = True
481 481 stop_now = False
482 482 def __init__(self, history_manager):
483 483 super(HistorySavingThread, self).__init__()
484 484 self.history_manager = history_manager
485 485 atexit.register(self.stop)
486 486
487 487 def run(self):
488 488 # We need a separate db connection per thread:
489 try:
489 490 self.db = sqlite3.connect(self.history_manager.hist_file)
490 491 while True:
491 492 self.history_manager.save_flag.wait()
492 493 if self.stop_now:
493 494 return
494 495 self.history_manager.save_flag.clear()
495 496 self.history_manager.writeout_cache(self.db)
497 except Exception as e:
498 print(("The history saving thread hit an unexpected error (%s)."
499 "History will not be written to the database.") % repr(e))
496 500
497 501 def stop(self):
498 502 """This can be called from the main thread to safely stop this thread.
499 503
500 504 Note that it does not attempt to write out remaining history before
501 505 exiting. That should be done by calling the HistoryManager's
502 506 end_session method."""
503 507 self.stop_now = True
504 508 self.history_manager.save_flag.set()
505 509 self.join()
506 510
507 511
508 512 # To match, e.g. ~5/8-~2/3
509 513 range_re = re.compile(r"""
510 514 ((?P<startsess>~?\d+)/)?
511 515 (?P<start>\d+) # Only the start line num is compulsory
512 516 ((?P<sep>[\-:])
513 517 ((?P<endsess>~?\d+)/)?
514 518 (?P<end>\d+))?
515 519 $""", re.VERBOSE)
516 520
517 521 def extract_hist_ranges(ranges_str):
518 522 """Turn a string of history ranges into 3-tuples of (session, start, stop).
519 523
520 524 Examples
521 525 --------
522 526 list(extract_input_ranges("~8/5-~7/4 2"))
523 527 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
524 528 """
525 529 for range_str in ranges_str.split():
526 530 rmatch = range_re.match(range_str)
527 531 if not rmatch:
528 532 continue
529 533 start = int(rmatch.group("start"))
530 534 end = rmatch.group("end")
531 535 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
532 536 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
533 537 end += 1
534 538 startsess = rmatch.group("startsess") or "0"
535 539 endsess = rmatch.group("endsess") or startsess
536 540 startsess = int(startsess.replace("~","-"))
537 541 endsess = int(endsess.replace("~","-"))
538 542 assert endsess >= startsess
539 543
540 544 if endsess == startsess:
541 545 yield (startsess, start, end)
542 546 continue
543 547 # Multiple sessions in one range:
544 548 yield (startsess, start, None)
545 549 for sess in range(startsess+1, endsess):
546 550 yield (sess, 1, None)
547 551 yield (endsess, 1, end)
548 552
549 553 def _format_lineno(session, line):
550 554 """Helper function to format line numbers properly."""
551 555 if session == 0:
552 556 return str(line)
553 557 return "%s#%s" % (session, line)
554 558
555 559 @testdec.skip_doctest
556 560 def magic_history(self, parameter_s = ''):
557 561 """Print input history (_i<n> variables), with most recent last.
558 562
559 563 %history -> print at most 40 inputs (some may be multi-line)\\
560 564 %history n -> print at most n inputs\\
561 565 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
562 566
563 567 By default, input history is printed without line numbers so it can be
564 568 directly pasted into an editor. Use -n to show them.
565 569
566 570 Ranges of history can be indicated using the syntax:
567 571 4 : Line 4, current session
568 572 4-6 : Lines 4-6, current session
569 573 243/1-5: Lines 1-5, session 243
570 574 ~2/7 : Line 7, session 2 before current
571 575 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
572 576 of 6 sessions ago.
573 577 Multiple ranges can be entered, separated by spaces
574 578
575 579 The same syntax is used by %macro, %save, %edit, %rerun
576 580
577 581 Options:
578 582
579 583 -n: print line numbers for each input.
580 584 This feature is only available if numbered prompts are in use.
581 585
582 586 -o: also print outputs for each input.
583 587
584 588 -p: print classic '>>>' python prompts before each input. This is useful
585 589 for making documentation, and in conjunction with -o, for producing
586 590 doctest-ready output.
587 591
588 592 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
589 593
590 594 -t: print the 'translated' history, as IPython understands it. IPython
591 595 filters your input and converts it all into valid Python source before
592 596 executing it (things like magics or aliases are turned into function
593 597 calls, for example). With this option, you'll see the native history
594 598 instead of the user-entered version: '%cd /' will be seen as
595 599 'get_ipython().magic("%cd /")' instead of '%cd /'.
596 600
597 601 -g: treat the arg as a pattern to grep for in (full) history.
598 602 This includes the saved history (almost all commands ever written).
599 603 Use '%hist -g' to show full saved history (may be very long).
600 604
601 605 -l: get the last n lines from all sessions. Specify n as a single arg, or
602 606 the default is the last 10 lines.
603 607
604 608 -f FILENAME: instead of printing the output to the screen, redirect it to
605 609 the given file. The file is always overwritten, though IPython asks for
606 610 confirmation first if it already exists.
607 611
608 612 Examples
609 613 --------
610 614 ::
611 615
612 616 In [6]: %hist -n 4 6
613 617 4:a = 12
614 618 5:print a**2
615 619
616 620 """
617 621
618 622 if not self.shell.displayhook.do_full_cache:
619 623 print('This feature is only available if numbered prompts are in use.')
620 624 return
621 625 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
622 626
623 627 # For brevity
624 628 history_manager = self.shell.history_manager
625 629
626 630 def _format_lineno(session, line):
627 631 """Helper function to format line numbers properly."""
628 632 if session in (0, history_manager.session_number):
629 633 return str(line)
630 634 return "%s/%s" % (session, line)
631 635
632 636 # Check if output to specific file was requested.
633 637 try:
634 638 outfname = opts['f']
635 639 except KeyError:
636 640 outfile = IPython.utils.io.Term.cout # default
637 641 # We don't want to close stdout at the end!
638 642 close_at_end = False
639 643 else:
640 644 if os.path.exists(outfname):
641 645 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
642 646 print('Aborting.')
643 647 return
644 648
645 649 outfile = open(outfname,'w')
646 650 close_at_end = True
647 651
648 652 print_nums = 'n' in opts
649 653 get_output = 'o' in opts
650 654 pyprompts = 'p' in opts
651 655 # Raw history is the default
652 656 raw = not('t' in opts)
653 657
654 658 default_length = 40
655 659 pattern = None
656 660
657 661 if 'g' in opts: # Glob search
658 662 pattern = "*" + args + "*" if args else "*"
659 663 hist = history_manager.search(pattern, raw=raw, output=get_output)
660 664 elif 'l' in opts: # Get 'tail'
661 665 try:
662 666 n = int(args)
663 667 except ValueError, IndexError:
664 668 n = 10
665 669 hist = history_manager.get_tail(n, raw=raw, output=get_output)
666 670 else:
667 671 if args: # Get history by ranges
668 672 hist = history_manager.get_range_by_str(args, raw, get_output)
669 673 else: # Just get history for the current session
670 674 hist = history_manager.get_range(raw=raw, output=get_output)
671 675
672 676 # We could be displaying the entire history, so let's not try to pull it
673 677 # into a list in memory. Anything that needs more space will just misalign.
674 678 width = 4
675 679
676 680 for session, lineno, inline in hist:
677 681 # Print user history with tabs expanded to 4 spaces. The GUI clients
678 682 # use hard tabs for easier usability in auto-indented code, but we want
679 683 # to produce PEP-8 compliant history for safe pasting into an editor.
680 684 if get_output:
681 685 inline, output = inline
682 686 inline = inline.expandtabs(4).rstrip()
683 687
684 688 multiline = "\n" in inline
685 689 line_sep = '\n' if multiline else ' '
686 690 if print_nums:
687 691 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
688 692 line_sep), file=outfile, end='')
689 693 if pyprompts:
690 694 print(">>> ", end="", file=outfile)
691 695 if multiline:
692 696 inline = "\n... ".join(inline.splitlines()) + "\n..."
693 697 print(inline, file=outfile)
694 698 if get_output and output:
695 699 print("\n".join(output), file=outfile)
696 700
697 701 if close_at_end:
698 702 outfile.close()
699 703
700 704
701 705 def magic_rep(self, arg):
702 706 r""" Repeat a command, or get command to input line for editing
703 707
704 708 - %rep (no arguments):
705 709
706 710 Place a string version of last computation result (stored in the special '_'
707 711 variable) to the next input prompt. Allows you to create elaborate command
708 712 lines without using copy-paste::
709 713
710 714 In[1]: l = ["hei", "vaan"]
711 715 In[2]: "".join(l)
712 716 Out[2]: heivaan
713 717 In[3]: %rep
714 718 In[4]: heivaan_ <== cursor blinking
715 719
716 720 %rep 45
717 721
718 722 Place history line 45 on the next input prompt. Use %hist to find
719 723 out the number.
720 724
721 725 %rep 1-4
722 726
723 727 Combine the specified lines into one cell, and place it on the next
724 728 input prompt. See %history for the slice syntax.
725 729
726 730 %rep foo+bar
727 731
728 732 If foo+bar can be evaluated in the user namespace, the result is
729 733 placed at the next input prompt. Otherwise, the history is searched
730 734 for lines which contain that substring, and the most recent one is
731 735 placed at the next input prompt.
732 736 """
733 737 if not arg: # Last output
734 738 self.set_next_input(str(self.shell.user_ns["_"]))
735 739 return
736 740 # Get history range
737 741 histlines = self.history_manager.get_range_by_str(arg)
738 742 cmd = "\n".join(x[2] for x in histlines)
739 743 if cmd:
740 744 self.set_next_input(cmd.rstrip())
741 745 return
742 746
743 747 try: # Variable in user namespace
744 748 cmd = str(eval(arg, self.shell.user_ns))
745 749 except Exception: # Search for term in history
746 750 histlines = self.history_manager.search("*"+arg+"*")
747 751 for h in reversed([x[2] for x in histlines]):
748 752 if 'rep' in h:
749 753 continue
750 754 self.set_next_input(h.rstrip())
751 755 return
752 756 else:
753 757 self.set_next_input(cmd.rstrip())
754 758 print("Couldn't evaluate or find in history:", arg)
755 759
756 760 def magic_rerun(self, parameter_s=''):
757 761 """Re-run previous input
758 762
759 763 By default, you can specify ranges of input history to be repeated
760 764 (as with %history). With no arguments, it will repeat the last line.
761 765
762 766 Options:
763 767
764 768 -l <n> : Repeat the last n lines of input, not including the
765 769 current command.
766 770
767 771 -g foo : Repeat the most recent line which contains foo
768 772 """
769 773 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
770 774 if "l" in opts: # Last n lines
771 775 n = int(opts['l'])
772 776 hist = self.history_manager.get_tail(n)
773 777 elif "g" in opts: # Search
774 778 p = "*"+opts['g']+"*"
775 779 hist = list(self.history_manager.search(p))
776 780 for l in reversed(hist):
777 781 if "rerun" not in l[2]:
778 782 hist = [l] # The last match which isn't a %rerun
779 783 break
780 784 else:
781 785 hist = [] # No matches except %rerun
782 786 elif args: # Specify history ranges
783 787 hist = self.history_manager.get_range_by_str(args)
784 788 else: # Last line
785 789 hist = self.history_manager.get_tail(1)
786 790 hist = [x[2] for x in hist]
787 791 if not hist:
788 792 print("No lines in history match specification")
789 793 return
790 794 histlines = "\n".join(hist)
791 795 print("=== Executing: ===")
792 796 print(histlines)
793 797 print("=== Output: ===")
794 798 self.run_cell("\n".join(hist), store_history=False)
795 799
796 800
797 801 def init_ipython(ip):
798 802 ip.define_magic("rep", magic_rep)
799 803 ip.define_magic("recall", magic_rep)
800 804 ip.define_magic("rerun", magic_rerun)
801 805 ip.define_magic("hist",magic_history) # Alternative name
802 806 ip.define_magic("history",magic_history)
803 807
804 808 # XXX - ipy_completers are in quarantine, need to be updated to new apis
805 809 #import ipy_completers
806 810 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
General Comments 0
You need to be logged in to leave comments. Login now