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