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