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