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