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