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