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