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