##// END OF EJS Templates
Add docstrings, improve history thread stop method.
Thomas Kluyver -
Show More
@@ -1,794 +1,806 b''
1 """ History related magics and functionality """
1 """ History related magics and functionality """
2 #-----------------------------------------------------------------------------
2 #-----------------------------------------------------------------------------
3 # Copyright (C) 2010 The IPython Development Team.
3 # Copyright (C) 2010 The IPython Development Team.
4 #
4 #
5 # Distributed under the terms of the BSD License.
5 # Distributed under the terms of the BSD License.
6 #
6 #
7 # The full license is in the file COPYING.txt, distributed with this software.
7 # The full license is in the file COPYING.txt, distributed with this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 from __future__ import print_function
13 from __future__ import print_function
14
14
15 # Stdlib imports
15 # Stdlib imports
16 import atexit
16 import atexit
17 import datetime
17 import datetime
18 import json
18 import json
19 import os
19 import os
20 import re
20 import re
21 import sqlite3
21 import sqlite3
22 import threading
22 import threading
23
23
24 from collections import defaultdict
24 from collections import defaultdict
25 from contextlib import nested
25 from contextlib import nested
26
26
27 # Our own packages
27 # Our own packages
28 from IPython.config.configurable import Configurable
28 from IPython.config.configurable import Configurable
29 import IPython.utils.io
29 import IPython.utils.io
30
30
31 from IPython.testing import decorators as testdec
31 from IPython.testing import decorators as testdec
32 from IPython.utils.io import ask_yes_no
32 from IPython.utils.io import ask_yes_no
33 from IPython.utils.traitlets import Bool, Dict, Instance, Int, List, Unicode
33 from IPython.utils.traitlets import Bool, Dict, Instance, Int, List, Unicode
34 from IPython.utils.warn import warn
34 from IPython.utils.warn import warn
35
35
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37 # Classes and functions
37 # Classes and functions
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39
39
40 class HistoryManager(Configurable):
40 class HistoryManager(Configurable):
41 """A class to organize all history-related functionality in one place.
41 """A class to organize all history-related functionality in one place.
42 """
42 """
43 # Public interface
43 # Public interface
44
44
45 # An instance of the IPython shell we are attached to
45 # An instance of the IPython shell we are attached to
46 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
46 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
47 # Lists to hold processed and raw history. These start with a blank entry
47 # Lists to hold processed and raw history. These start with a blank entry
48 # so that we can index them starting from 1
48 # so that we can index them starting from 1
49 input_hist_parsed = List([""])
49 input_hist_parsed = List([""])
50 input_hist_raw = List([""])
50 input_hist_raw = List([""])
51 # A list of directories visited during session
51 # A list of directories visited during session
52 dir_hist = List()
52 dir_hist = List()
53 def _dir_hist_default(self):
53 def _dir_hist_default(self):
54 try:
54 try:
55 return [os.getcwd()]
55 return [os.getcwd()]
56 except OSError:
56 except OSError:
57 return []
57 return []
58
58
59 # A dict of output history, keyed with ints from the shell's
59 # A dict of output history, keyed with ints from the shell's
60 # execution count. If there are several outputs from one command,
60 # execution count. If there are several outputs from one command,
61 # only the last one is stored.
61 # only the last one is stored.
62 output_hist = Dict()
62 output_hist = Dict()
63 # Contains all outputs, in lists of reprs.
63 # Contains all outputs, in lists of reprs.
64 output_hist_reprs = Instance(defaultdict, args=(list,))
64 output_hist_reprs = Instance(defaultdict, args=(list,))
65
65
66 # String holding the path to the history file
66 # String holding the path to the history file
67 hist_file = Unicode(config=True)
67 hist_file = Unicode(config=True)
68
68
69 # The SQLite database
69 # The SQLite database
70 db = Instance(sqlite3.Connection)
70 db = Instance(sqlite3.Connection)
71 # The number of the current session in the history database
71 # The number of the current session in the history database
72 session_number = Int()
72 session_number = Int()
73 # Should we log output to the database? (default no)
73 # Should we log output to the database? (default no)
74 db_log_output = Bool(False, config=True)
74 db_log_output = Bool(False, config=True)
75 # Write to database every x commands (higher values save disk access & power)
75 # Write to database every x commands (higher values save disk access & power)
76 # Values of 1 or less effectively disable caching.
76 # Values of 1 or less effectively disable caching.
77 db_cache_size = Int(0, config=True)
77 db_cache_size = Int(0, config=True)
78 # The input and output caches
78 # The input and output caches
79 db_input_cache = List()
79 db_input_cache = List()
80 db_output_cache = List()
80 db_output_cache = List()
81
81
82 # History saving in separate thread
82 # History saving in separate thread
83 save_thread = Instance('IPython.core.history.HistorySavingThread')
83 save_thread = Instance('IPython.core.history.HistorySavingThread')
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
475 the UI isn't held up while that happens.
476
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
479 the cache size reaches a defined threshold."""
474 daemon = True
480 daemon = True
475 stop_now = False
481 stop_now = False
476 def __init__(self, history_manager):
482 def __init__(self, history_manager):
477 super(HistorySavingThread, self).__init__()
483 super(HistorySavingThread, self).__init__()
478 self.history_manager = history_manager
484 self.history_manager = history_manager
479 atexit.register(self.stop)
485 atexit.register(self.stop)
480
486
481 def run(self):
487 def run(self):
482 # We need a separate db connection per thread:
488 # We need a separate db connection per thread:
483 self.db = sqlite3.connect(self.history_manager.hist_file)
489 self.db = sqlite3.connect(self.history_manager.hist_file)
484 while True:
490 while True:
485 self.history_manager.save_flag.wait()
491 self.history_manager.save_flag.wait()
486 self.history_manager.save_flag.clear()
487 self.history_manager.writeout_cache(self.db)
488 if self.stop_now:
492 if self.stop_now:
489 return
493 return
494 self.history_manager.save_flag.clear()
495 self.history_manager.writeout_cache(self.db)
490
496
491 def stop(self):
497 def stop(self):
498 """This can be called from the main thread to safely stop this thread.
499
500 Note that it does not attempt to write out remaining history before
501 exiting. That should be done by calling the HistoryManager's
502 end_session method."""
492 self.stop_now = True
503 self.stop_now = True
493 self.history_manager.save_flag.set()
504 self.history_manager.save_flag.set()
505 self.join()
494
506
495
507
496 # To match, e.g. ~5/8-~2/3
508 # To match, e.g. ~5/8-~2/3
497 range_re = re.compile(r"""
509 range_re = re.compile(r"""
498 ((?P<startsess>~?\d+)/)?
510 ((?P<startsess>~?\d+)/)?
499 (?P<start>\d+) # Only the start line num is compulsory
511 (?P<start>\d+) # Only the start line num is compulsory
500 ((?P<sep>[\-:])
512 ((?P<sep>[\-:])
501 ((?P<endsess>~?\d+)/)?
513 ((?P<endsess>~?\d+)/)?
502 (?P<end>\d+))?
514 (?P<end>\d+))?
503 $""", re.VERBOSE)
515 $""", re.VERBOSE)
504
516
505 def extract_hist_ranges(ranges_str):
517 def extract_hist_ranges(ranges_str):
506 """Turn a string of history ranges into 3-tuples of (session, start, stop).
518 """Turn a string of history ranges into 3-tuples of (session, start, stop).
507
519
508 Examples
520 Examples
509 --------
521 --------
510 list(extract_input_ranges("~8/5-~7/4 2"))
522 list(extract_input_ranges("~8/5-~7/4 2"))
511 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
523 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
512 """
524 """
513 for range_str in ranges_str.split():
525 for range_str in ranges_str.split():
514 rmatch = range_re.match(range_str)
526 rmatch = range_re.match(range_str)
515 if not rmatch:
527 if not rmatch:
516 continue
528 continue
517 start = int(rmatch.group("start"))
529 start = int(rmatch.group("start"))
518 end = rmatch.group("end")
530 end = rmatch.group("end")
519 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
531 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
520 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
532 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
521 end += 1
533 end += 1
522 startsess = rmatch.group("startsess") or "0"
534 startsess = rmatch.group("startsess") or "0"
523 endsess = rmatch.group("endsess") or startsess
535 endsess = rmatch.group("endsess") or startsess
524 startsess = int(startsess.replace("~","-"))
536 startsess = int(startsess.replace("~","-"))
525 endsess = int(endsess.replace("~","-"))
537 endsess = int(endsess.replace("~","-"))
526 assert endsess >= startsess
538 assert endsess >= startsess
527
539
528 if endsess == startsess:
540 if endsess == startsess:
529 yield (startsess, start, end)
541 yield (startsess, start, end)
530 continue
542 continue
531 # Multiple sessions in one range:
543 # Multiple sessions in one range:
532 yield (startsess, start, None)
544 yield (startsess, start, None)
533 for sess in range(startsess+1, endsess):
545 for sess in range(startsess+1, endsess):
534 yield (sess, 1, None)
546 yield (sess, 1, None)
535 yield (endsess, 1, end)
547 yield (endsess, 1, end)
536
548
537 def _format_lineno(session, line):
549 def _format_lineno(session, line):
538 """Helper function to format line numbers properly."""
550 """Helper function to format line numbers properly."""
539 if session == 0:
551 if session == 0:
540 return str(line)
552 return str(line)
541 return "%s#%s" % (session, line)
553 return "%s#%s" % (session, line)
542
554
543 @testdec.skip_doctest
555 @testdec.skip_doctest
544 def magic_history(self, parameter_s = ''):
556 def magic_history(self, parameter_s = ''):
545 """Print input history (_i<n> variables), with most recent last.
557 """Print input history (_i<n> variables), with most recent last.
546
558
547 %history -> print at most 40 inputs (some may be multi-line)\\
559 %history -> print at most 40 inputs (some may be multi-line)\\
548 %history n -> print at most n inputs\\
560 %history n -> print at most n inputs\\
549 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
561 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
550
562
551 By default, input history is printed without line numbers so it can be
563 By default, input history is printed without line numbers so it can be
552 directly pasted into an editor. Use -n to show them.
564 directly pasted into an editor. Use -n to show them.
553
565
554 Ranges of history can be indicated using the syntax:
566 Ranges of history can be indicated using the syntax:
555 4 : Line 4, current session
567 4 : Line 4, current session
556 4-6 : Lines 4-6, current session
568 4-6 : Lines 4-6, current session
557 243/1-5: Lines 1-5, session 243
569 243/1-5: Lines 1-5, session 243
558 ~2/7 : Line 7, session 2 before current
570 ~2/7 : Line 7, session 2 before current
559 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
571 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
560 of 6 sessions ago.
572 of 6 sessions ago.
561 Multiple ranges can be entered, separated by spaces
573 Multiple ranges can be entered, separated by spaces
562
574
563 The same syntax is used by %macro, %save, %edit, %rerun
575 The same syntax is used by %macro, %save, %edit, %rerun
564
576
565 Options:
577 Options:
566
578
567 -n: print line numbers for each input.
579 -n: print line numbers for each input.
568 This feature is only available if numbered prompts are in use.
580 This feature is only available if numbered prompts are in use.
569
581
570 -o: also print outputs for each input.
582 -o: also print outputs for each input.
571
583
572 -p: print classic '>>>' python prompts before each input. This is useful
584 -p: print classic '>>>' python prompts before each input. This is useful
573 for making documentation, and in conjunction with -o, for producing
585 for making documentation, and in conjunction with -o, for producing
574 doctest-ready output.
586 doctest-ready output.
575
587
576 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
588 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
577
589
578 -t: print the 'translated' history, as IPython understands it. IPython
590 -t: print the 'translated' history, as IPython understands it. IPython
579 filters your input and converts it all into valid Python source before
591 filters your input and converts it all into valid Python source before
580 executing it (things like magics or aliases are turned into function
592 executing it (things like magics or aliases are turned into function
581 calls, for example). With this option, you'll see the native history
593 calls, for example). With this option, you'll see the native history
582 instead of the user-entered version: '%cd /' will be seen as
594 instead of the user-entered version: '%cd /' will be seen as
583 'get_ipython().magic("%cd /")' instead of '%cd /'.
595 'get_ipython().magic("%cd /")' instead of '%cd /'.
584
596
585 -g: treat the arg as a pattern to grep for in (full) history.
597 -g: treat the arg as a pattern to grep for in (full) history.
586 This includes the saved history (almost all commands ever written).
598 This includes the saved history (almost all commands ever written).
587 Use '%hist -g' to show full saved history (may be very long).
599 Use '%hist -g' to show full saved history (may be very long).
588
600
589 -l: get the last n lines from all sessions. Specify n as a single arg, or
601 -l: get the last n lines from all sessions. Specify n as a single arg, or
590 the default is the last 10 lines.
602 the default is the last 10 lines.
591
603
592 -f FILENAME: instead of printing the output to the screen, redirect it to
604 -f FILENAME: instead of printing the output to the screen, redirect it to
593 the given file. The file is always overwritten, though IPython asks for
605 the given file. The file is always overwritten, though IPython asks for
594 confirmation first if it already exists.
606 confirmation first if it already exists.
595
607
596 Examples
608 Examples
597 --------
609 --------
598 ::
610 ::
599
611
600 In [6]: %hist -n 4 6
612 In [6]: %hist -n 4 6
601 4:a = 12
613 4:a = 12
602 5:print a**2
614 5:print a**2
603
615
604 """
616 """
605
617
606 if not self.shell.displayhook.do_full_cache:
618 if not self.shell.displayhook.do_full_cache:
607 print('This feature is only available if numbered prompts are in use.')
619 print('This feature is only available if numbered prompts are in use.')
608 return
620 return
609 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
621 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
610
622
611 # For brevity
623 # For brevity
612 history_manager = self.shell.history_manager
624 history_manager = self.shell.history_manager
613
625
614 def _format_lineno(session, line):
626 def _format_lineno(session, line):
615 """Helper function to format line numbers properly."""
627 """Helper function to format line numbers properly."""
616 if session in (0, history_manager.session_number):
628 if session in (0, history_manager.session_number):
617 return str(line)
629 return str(line)
618 return "%s/%s" % (session, line)
630 return "%s/%s" % (session, line)
619
631
620 # Check if output to specific file was requested.
632 # Check if output to specific file was requested.
621 try:
633 try:
622 outfname = opts['f']
634 outfname = opts['f']
623 except KeyError:
635 except KeyError:
624 outfile = IPython.utils.io.Term.cout # default
636 outfile = IPython.utils.io.Term.cout # default
625 # We don't want to close stdout at the end!
637 # We don't want to close stdout at the end!
626 close_at_end = False
638 close_at_end = False
627 else:
639 else:
628 if os.path.exists(outfname):
640 if os.path.exists(outfname):
629 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
641 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
630 print('Aborting.')
642 print('Aborting.')
631 return
643 return
632
644
633 outfile = open(outfname,'w')
645 outfile = open(outfname,'w')
634 close_at_end = True
646 close_at_end = True
635
647
636 print_nums = 'n' in opts
648 print_nums = 'n' in opts
637 get_output = 'o' in opts
649 get_output = 'o' in opts
638 pyprompts = 'p' in opts
650 pyprompts = 'p' in opts
639 # Raw history is the default
651 # Raw history is the default
640 raw = not('t' in opts)
652 raw = not('t' in opts)
641
653
642 default_length = 40
654 default_length = 40
643 pattern = None
655 pattern = None
644
656
645 if 'g' in opts: # Glob search
657 if 'g' in opts: # Glob search
646 pattern = "*" + args + "*" if args else "*"
658 pattern = "*" + args + "*" if args else "*"
647 hist = history_manager.search(pattern, raw=raw, output=get_output)
659 hist = history_manager.search(pattern, raw=raw, output=get_output)
648 elif 'l' in opts: # Get 'tail'
660 elif 'l' in opts: # Get 'tail'
649 try:
661 try:
650 n = int(args)
662 n = int(args)
651 except ValueError, IndexError:
663 except ValueError, IndexError:
652 n = 10
664 n = 10
653 hist = history_manager.get_tail(n, raw=raw, output=get_output)
665 hist = history_manager.get_tail(n, raw=raw, output=get_output)
654 else:
666 else:
655 if args: # Get history by ranges
667 if args: # Get history by ranges
656 hist = history_manager.get_range_by_str(args, raw, get_output)
668 hist = history_manager.get_range_by_str(args, raw, get_output)
657 else: # Just get history for the current session
669 else: # Just get history for the current session
658 hist = history_manager.get_range(raw=raw, output=get_output)
670 hist = history_manager.get_range(raw=raw, output=get_output)
659
671
660 # We could be displaying the entire history, so let's not try to pull it
672 # We could be displaying the entire history, so let's not try to pull it
661 # into a list in memory. Anything that needs more space will just misalign.
673 # into a list in memory. Anything that needs more space will just misalign.
662 width = 4
674 width = 4
663
675
664 for session, lineno, inline in hist:
676 for session, lineno, inline in hist:
665 # Print user history with tabs expanded to 4 spaces. The GUI clients
677 # Print user history with tabs expanded to 4 spaces. The GUI clients
666 # use hard tabs for easier usability in auto-indented code, but we want
678 # use hard tabs for easier usability in auto-indented code, but we want
667 # to produce PEP-8 compliant history for safe pasting into an editor.
679 # to produce PEP-8 compliant history for safe pasting into an editor.
668 if get_output:
680 if get_output:
669 inline, output = inline
681 inline, output = inline
670 inline = inline.expandtabs(4).rstrip()
682 inline = inline.expandtabs(4).rstrip()
671
683
672 multiline = "\n" in inline
684 multiline = "\n" in inline
673 line_sep = '\n' if multiline else ' '
685 line_sep = '\n' if multiline else ' '
674 if print_nums:
686 if print_nums:
675 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
687 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
676 line_sep), file=outfile, end='')
688 line_sep), file=outfile, end='')
677 if pyprompts:
689 if pyprompts:
678 print(">>> ", end="", file=outfile)
690 print(">>> ", end="", file=outfile)
679 if multiline:
691 if multiline:
680 inline = "\n... ".join(inline.splitlines()) + "\n..."
692 inline = "\n... ".join(inline.splitlines()) + "\n..."
681 print(inline, file=outfile)
693 print(inline, file=outfile)
682 if get_output and output:
694 if get_output and output:
683 print("\n".join(output), file=outfile)
695 print("\n".join(output), file=outfile)
684
696
685 if close_at_end:
697 if close_at_end:
686 outfile.close()
698 outfile.close()
687
699
688
700
689 def magic_rep(self, arg):
701 def magic_rep(self, arg):
690 r""" Repeat a command, or get command to input line for editing
702 r""" Repeat a command, or get command to input line for editing
691
703
692 - %rep (no arguments):
704 - %rep (no arguments):
693
705
694 Place a string version of last computation result (stored in the special '_'
706 Place a string version of last computation result (stored in the special '_'
695 variable) to the next input prompt. Allows you to create elaborate command
707 variable) to the next input prompt. Allows you to create elaborate command
696 lines without using copy-paste::
708 lines without using copy-paste::
697
709
698 In[1]: l = ["hei", "vaan"]
710 In[1]: l = ["hei", "vaan"]
699 In[2]: "".join(l)
711 In[2]: "".join(l)
700 Out[2]: heivaan
712 Out[2]: heivaan
701 In[3]: %rep
713 In[3]: %rep
702 In[4]: heivaan_ <== cursor blinking
714 In[4]: heivaan_ <== cursor blinking
703
715
704 %rep 45
716 %rep 45
705
717
706 Place history line 45 on the next input prompt. Use %hist to find
718 Place history line 45 on the next input prompt. Use %hist to find
707 out the number.
719 out the number.
708
720
709 %rep 1-4
721 %rep 1-4
710
722
711 Combine the specified lines into one cell, and place it on the next
723 Combine the specified lines into one cell, and place it on the next
712 input prompt. See %history for the slice syntax.
724 input prompt. See %history for the slice syntax.
713
725
714 %rep foo+bar
726 %rep foo+bar
715
727
716 If foo+bar can be evaluated in the user namespace, the result is
728 If foo+bar can be evaluated in the user namespace, the result is
717 placed at the next input prompt. Otherwise, the history is searched
729 placed at the next input prompt. Otherwise, the history is searched
718 for lines which contain that substring, and the most recent one is
730 for lines which contain that substring, and the most recent one is
719 placed at the next input prompt.
731 placed at the next input prompt.
720 """
732 """
721 if not arg: # Last output
733 if not arg: # Last output
722 self.set_next_input(str(self.shell.user_ns["_"]))
734 self.set_next_input(str(self.shell.user_ns["_"]))
723 return
735 return
724 # Get history range
736 # Get history range
725 histlines = self.history_manager.get_range_by_str(arg)
737 histlines = self.history_manager.get_range_by_str(arg)
726 cmd = "\n".join(x[2] for x in histlines)
738 cmd = "\n".join(x[2] for x in histlines)
727 if cmd:
739 if cmd:
728 self.set_next_input(cmd.rstrip())
740 self.set_next_input(cmd.rstrip())
729 return
741 return
730
742
731 try: # Variable in user namespace
743 try: # Variable in user namespace
732 cmd = str(eval(arg, self.shell.user_ns))
744 cmd = str(eval(arg, self.shell.user_ns))
733 except Exception: # Search for term in history
745 except Exception: # Search for term in history
734 histlines = self.history_manager.search("*"+arg+"*")
746 histlines = self.history_manager.search("*"+arg+"*")
735 for h in reversed([x[2] for x in histlines]):
747 for h in reversed([x[2] for x in histlines]):
736 if 'rep' in h:
748 if 'rep' in h:
737 continue
749 continue
738 self.set_next_input(h.rstrip())
750 self.set_next_input(h.rstrip())
739 return
751 return
740 else:
752 else:
741 self.set_next_input(cmd.rstrip())
753 self.set_next_input(cmd.rstrip())
742 print("Couldn't evaluate or find in history:", arg)
754 print("Couldn't evaluate or find in history:", arg)
743
755
744 def magic_rerun(self, parameter_s=''):
756 def magic_rerun(self, parameter_s=''):
745 """Re-run previous input
757 """Re-run previous input
746
758
747 By default, you can specify ranges of input history to be repeated
759 By default, you can specify ranges of input history to be repeated
748 (as with %history). With no arguments, it will repeat the last line.
760 (as with %history). With no arguments, it will repeat the last line.
749
761
750 Options:
762 Options:
751
763
752 -l <n> : Repeat the last n lines of input, not including the
764 -l <n> : Repeat the last n lines of input, not including the
753 current command.
765 current command.
754
766
755 -g foo : Repeat the most recent line which contains foo
767 -g foo : Repeat the most recent line which contains foo
756 """
768 """
757 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
769 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
758 if "l" in opts: # Last n lines
770 if "l" in opts: # Last n lines
759 n = int(opts['l'])
771 n = int(opts['l'])
760 hist = self.history_manager.get_tail(n)
772 hist = self.history_manager.get_tail(n)
761 elif "g" in opts: # Search
773 elif "g" in opts: # Search
762 p = "*"+opts['g']+"*"
774 p = "*"+opts['g']+"*"
763 hist = list(self.history_manager.search(p))
775 hist = list(self.history_manager.search(p))
764 for l in reversed(hist):
776 for l in reversed(hist):
765 if "rerun" not in l[2]:
777 if "rerun" not in l[2]:
766 hist = [l] # The last match which isn't a %rerun
778 hist = [l] # The last match which isn't a %rerun
767 break
779 break
768 else:
780 else:
769 hist = [] # No matches except %rerun
781 hist = [] # No matches except %rerun
770 elif args: # Specify history ranges
782 elif args: # Specify history ranges
771 hist = self.history_manager.get_range_by_str(args)
783 hist = self.history_manager.get_range_by_str(args)
772 else: # Last line
784 else: # Last line
773 hist = self.history_manager.get_tail(1)
785 hist = self.history_manager.get_tail(1)
774 hist = [x[2] for x in hist]
786 hist = [x[2] for x in hist]
775 if not hist:
787 if not hist:
776 print("No lines in history match specification")
788 print("No lines in history match specification")
777 return
789 return
778 histlines = "\n".join(hist)
790 histlines = "\n".join(hist)
779 print("=== Executing: ===")
791 print("=== Executing: ===")
780 print(histlines)
792 print(histlines)
781 print("=== Output: ===")
793 print("=== Output: ===")
782 self.run_cell("\n".join(hist), store_history=False)
794 self.run_cell("\n".join(hist), store_history=False)
783
795
784
796
785 def init_ipython(ip):
797 def init_ipython(ip):
786 ip.define_magic("rep", magic_rep)
798 ip.define_magic("rep", magic_rep)
787 ip.define_magic("recall", magic_rep)
799 ip.define_magic("recall", magic_rep)
788 ip.define_magic("rerun", magic_rerun)
800 ip.define_magic("rerun", magic_rerun)
789 ip.define_magic("hist",magic_history) # Alternative name
801 ip.define_magic("hist",magic_history) # Alternative name
790 ip.define_magic("history",magic_history)
802 ip.define_magic("history",magic_history)
791
803
792 # XXX - ipy_completers are in quarantine, need to be updated to new apis
804 # XXX - ipy_completers are in quarantine, need to be updated to new apis
793 #import ipy_completers
805 #import ipy_completers
794 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
806 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
General Comments 0
You need to be logged in to leave comments. Login now