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