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