##// END OF EJS Templates
Flush cache before querying database.
Thomas Kluyver -
Show More
@@ -1,596 +1,598 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 os
16 import os
17 import re
17 import re
18 import sqlite3
18 import sqlite3
19
19
20 # Our own packages
20 # Our own packages
21 from IPython.config.configurable import Configurable
21 from IPython.config.configurable import Configurable
22 import IPython.utils.io
22 import IPython.utils.io
23
23
24 from IPython.testing import decorators as testdec
24 from IPython.testing import decorators as testdec
25 from IPython.utils.io import ask_yes_no
25 from IPython.utils.io import ask_yes_no
26 from IPython.utils.traitlets import Bool, Dict, Instance, Int, List, Unicode
26 from IPython.utils.traitlets import Bool, Dict, Instance, Int, List, Unicode
27 from IPython.utils.warn import warn
27 from IPython.utils.warn import warn
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Classes and functions
30 # Classes and functions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 class HistoryManager(Configurable):
33 class HistoryManager(Configurable):
34 """A class to organize all history-related functionality in one place.
34 """A class to organize all history-related functionality in one place.
35 """
35 """
36 # Public interface
36 # Public interface
37
37
38 # An instance of the IPython shell we are attached to
38 # An instance of the IPython shell we are attached to
39 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
39 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
40 # Lists to hold processed and raw history. These start with a blank entry
40 # Lists to hold processed and raw history. These start with a blank entry
41 # so that we can index them starting from 1
41 # so that we can index them starting from 1
42 input_hist_parsed = List([""])
42 input_hist_parsed = List([""])
43 input_hist_raw = List([""])
43 input_hist_raw = List([""])
44 # A list of directories visited during session
44 # A list of directories visited during session
45 dir_hist = List()
45 dir_hist = List()
46 # A dict of output history, keyed with ints from the shell's execution count
46 # A dict of output history, keyed with ints from the shell's execution count
47 output_hist = Dict()
47 output_hist = Dict()
48 # String holding the path to the history file
48 # String holding the path to the history file
49 hist_file = Unicode()
49 hist_file = Unicode()
50 # The SQLite database
50 # The SQLite database
51 db = Instance(sqlite3.Connection)
51 db = Instance(sqlite3.Connection)
52 # The number of the current session in the history database
52 # The number of the current session in the history database
53 session_number = Int()
53 session_number = Int()
54 # Should we log output to the database? (default no)
54 # Should we log output to the database? (default no)
55 db_log_output = Bool(False, config=True)
55 db_log_output = Bool(False, config=True)
56 # Write to database every x commands (higher values save disk access & power)
56 # Write to database every x commands (higher values save disk access & power)
57 # Values of 1 or less effectively disable caching.
57 # Values of 1 or less effectively disable caching.
58 db_cache_size = Int(0, config=True)
58 db_cache_size = Int(0, config=True)
59 # The input and output caches
59 # The input and output caches
60 db_input_cache = List()
60 db_input_cache = List()
61 db_output_cache = List()
61 db_output_cache = List()
62
62
63 # Private interface
63 # Private interface
64 # Variables used to store the three last inputs from the user. On each new
64 # Variables used to store the three last inputs from the user. On each new
65 # history update, we populate the user's namespace with these, shifted as
65 # history update, we populate the user's namespace with these, shifted as
66 # necessary.
66 # necessary.
67 _i00, _i, _ii, _iii = '','','',''
67 _i00, _i, _ii, _iii = '','','',''
68
68
69 # A set with all forms of the exit command, so that we don't store them in
69 # A set with all forms of the exit command, so that we don't store them in
70 # the history (it's annoying to rewind the first entry and land on an exit
70 # the history (it's annoying to rewind the first entry and land on an exit
71 # call).
71 # call).
72 _exit_commands = None
72 _exit_commands = None
73
73
74 def __init__(self, shell, config=None):
74 def __init__(self, shell, config=None):
75 """Create a new history manager associated with a shell instance.
75 """Create a new history manager associated with a shell instance.
76 """
76 """
77 # We need a pointer back to the shell for various tasks.
77 # We need a pointer back to the shell for various tasks.
78 super(HistoryManager, self).__init__(shell=shell, config=config)
78 super(HistoryManager, self).__init__(shell=shell, config=config)
79
79
80 # list of visited directories
80 # list of visited directories
81 try:
81 try:
82 self.dir_hist = [os.getcwd()]
82 self.dir_hist = [os.getcwd()]
83 except OSError:
83 except OSError:
84 self.dir_hist = []
84 self.dir_hist = []
85
85
86 # Now the history file
86 # Now the history file
87 if shell.profile:
87 if shell.profile:
88 histfname = 'history-%s' % shell.profile
88 histfname = 'history-%s' % shell.profile
89 else:
89 else:
90 histfname = 'history'
90 histfname = 'history'
91 self.hist_file = os.path.join(shell.ipython_dir, histfname + '.sqlite')
91 self.hist_file = os.path.join(shell.ipython_dir, histfname + '.sqlite')
92 self.init_db()
92 self.init_db()
93
93
94 self._i00, self._i, self._ii, self._iii = '','','',''
94 self._i00, self._i, self._ii, self._iii = '','','',''
95
95
96 self._exit_commands = set(['Quit', 'quit', 'Exit', 'exit', '%Quit',
96 self._exit_commands = set(['Quit', 'quit', 'Exit', 'exit', '%Quit',
97 '%quit', '%Exit', '%exit'])
97 '%quit', '%Exit', '%exit'])
98
98
99 def init_db(self):
99 def init_db(self):
100 """Connect to the database and get new session number."""
100 """Connect to the database and get new session number."""
101 self.db = sqlite3.connect(self.hist_file)
101 self.db = sqlite3.connect(self.hist_file)
102 self.db.execute("""CREATE TABLE IF NOT EXISTS history
102 self.db.execute("""CREATE TABLE IF NOT EXISTS history
103 (session integer, line integer, source text, source_raw text,
103 (session integer, line integer, source text, source_raw text,
104 PRIMARY KEY (session, line))""")
104 PRIMARY KEY (session, line))""")
105 # Output history is optional, but ensure the table's there so it can be
105 # Output history is optional, but ensure the table's there so it can be
106 # enabled later.
106 # enabled later.
107 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
107 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
108 (session integer, line integer, output text,
108 (session integer, line integer, output text,
109 PRIMARY KEY (session, line))""")
109 PRIMARY KEY (session, line))""")
110 cur = self.db.execute("""SELECT name FROM sqlite_master WHERE
110 cur = self.db.execute("""SELECT name FROM sqlite_master WHERE
111 type='table' AND name='singletons'""")
111 type='table' AND name='singletons'""")
112 if not cur.fetchone():
112 if not cur.fetchone():
113 self.db.execute("""CREATE TABLE singletons
113 self.db.execute("""CREATE TABLE singletons
114 (name text PRIMARY KEY, value)""")
114 (name text PRIMARY KEY, value)""")
115 self.db.execute("""INSERT INTO singletons VALUES
115 self.db.execute("""INSERT INTO singletons VALUES
116 ('session_number', 1)""")
116 ('session_number', 1)""")
117 self.db.commit()
117 self.db.commit()
118 cur = self.db.execute("""SELECT value FROM singletons WHERE
118 cur = self.db.execute("""SELECT value FROM singletons WHERE
119 name='session_number'""")
119 name='session_number'""")
120 self.session_number = cur.fetchone()[0]
120 self.session_number = cur.fetchone()[0]
121
121
122 #Increment by one for next session.
122 #Increment by one for next session.
123 self.db.execute("""UPDATE singletons SET value=? WHERE
123 self.db.execute("""UPDATE singletons SET value=? WHERE
124 name='session_number'""", (self.session_number+1,))
124 name='session_number'""", (self.session_number+1,))
125 self.db.commit()
125 self.db.commit()
126
126
127 def _get_hist_sql(self, sql, params, raw=True, output=False):
127 def _get_hist_sql(self, sql, params, raw=True, output=False):
128 """Prepares and runs an SQL query for the history database.
128 """Prepares and runs an SQL query for the history database.
129
129
130 Parameters
130 Parameters
131 ----------
131 ----------
132 sql : str
132 sql : str
133 Any filtering expressions to go after SELECT ... FROM ...
133 Any filtering expressions to go after SELECT ... FROM ...
134 params : tuple
134 params : tuple
135 Parameters passed to the SQL query (to replace "?")
135 Parameters passed to the SQL query (to replace "?")
136 raw : bool
136 raw : bool
137 If True, get raw input.
137 If True, get raw input.
138 output : bool
138 output : bool
139 If True, include output where available.
139 If True, include output where available.
140
140
141 Returns
141 Returns
142 -------
142 -------
143 An iterator over 3-tuples: (session, line_number, command), or if output
143 An iterator over 3-tuples: (session, line_number, command), or if output
144 is True, (session, line_number, (command, output)).
144 is True, (session, line_number, (command, output)).
145 """
145 """
146 toget = 'source_raw' if raw else 'source'
146 toget = 'source_raw' if raw else 'source'
147 sqlfrom = "history"
147 sqlfrom = "history"
148 if output:
148 if output:
149 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
149 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
150 toget = "history.%s, output_history.output" % toget
150 toget = "history.%s, output_history.output" % toget
151 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
151 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
152 (toget, sqlfrom) + sql, params)
152 (toget, sqlfrom) + sql, params)
153 if output: # Regroup into 3-tuples
153 if output: # Regroup into 3-tuples
154 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
154 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
155 return cur
155 return cur
156
156
157
157
158 def get_hist_tail(self, n=10, raw=True, output=False):
158 def get_hist_tail(self, n=10, raw=True, output=False):
159 """Get the last n lines from the history database."""
159 """Get the last n lines from the history database."""
160 self.writeout_cache()
160 cur = self._get_hist_sql("ORDER BY session DESC, line DESC LIMIT ?",
161 cur = self._get_hist_sql("ORDER BY session DESC, line DESC LIMIT ?",
161 (n,), raw=raw, output=output)
162 (n,), raw=raw, output=output)
162 return reversed(list(cur))
163 return reversed(list(cur))
163
164
164 def get_hist_search(self, pattern="*", raw=True, output=False):
165 def get_hist_search(self, pattern="*", raw=True, output=False):
165 """Search the database using unix glob-style matching (wildcards * and
166 """Search the database using unix glob-style matching (wildcards * and
166 ?, escape using \).
167 ?, escape using \).
167
168
168 Returns
169 Returns
169 -------
170 -------
170 An iterator over tuples: (session, line_number, command)
171 An iterator over tuples: (session, line_number, command)
171 """
172 """
172 tosearch = "source_raw" if raw else "source"
173 tosearch = "source_raw" if raw else "source"
173 if output:
174 if output:
174 tosearch = "history." + tosearch
175 tosearch = "history." + tosearch
176 self.writeout_cache()
175 return self._get_hist_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
177 return self._get_hist_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
176 raw=raw, output=output)
178 raw=raw, output=output)
177
179
178 def _get_hist_session(self, start=1, stop=None, raw=True, output=False):
180 def _get_hist_session(self, start=1, stop=None, raw=True, output=False):
179 """Get input and output history from the current session. Called by
181 """Get input and output history from the current session. Called by
180 get_history, and takes similar parameters."""
182 get_history, and takes similar parameters."""
181 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
183 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
182
184
183 n = len(input_hist)
185 n = len(input_hist)
184 if start < 0:
186 if start < 0:
185 start += n
187 start += n
186 if not stop:
188 if not stop:
187 stop = n
189 stop = n
188 elif stop < 0:
190 elif stop < 0:
189 stop += n
191 stop += n
190
192
191 for i in range(start, stop):
193 for i in range(start, stop):
192 if output:
194 if output:
193 line = (input_hist[i], repr(self.output_hist.get(i)))
195 line = (input_hist[i], repr(self.output_hist.get(i)))
194 else:
196 else:
195 line = input_hist[i]
197 line = input_hist[i]
196 yield (0, i, line)
198 yield (0, i, line)
197
199
198 def get_history(self, session=0, start=1, stop=None, raw=True,output=False):
200 def get_history(self, session=0, start=1, stop=None, raw=True,output=False):
199 """Retrieve input by session.
201 """Retrieve input by session.
200
202
201 Parameters
203 Parameters
202 ----------
204 ----------
203 session : int
205 session : int
204 Session number to retrieve. The current session is 0, and negative
206 Session number to retrieve. The current session is 0, and negative
205 numbers count back from current session, so -1 is previous session.
207 numbers count back from current session, so -1 is previous session.
206 start : int
208 start : int
207 First line to retrieve.
209 First line to retrieve.
208 stop : int
210 stop : int
209 End of line range (excluded from output itself). If None, retrieve
211 End of line range (excluded from output itself). If None, retrieve
210 to the end of the session.
212 to the end of the session.
211 raw : bool
213 raw : bool
212 If True, return untranslated input
214 If True, return untranslated input
213 output : bool
215 output : bool
214 If True, attempt to include output. This will be 'real' Python
216 If True, attempt to include output. This will be 'real' Python
215 objects for the current session, or text reprs from previous
217 objects for the current session, or text reprs from previous
216 sessions if db_log_output was enabled at the time. Where no output
218 sessions if db_log_output was enabled at the time. Where no output
217 is found, None is used.
219 is found, None is used.
218
220
219 Returns
221 Returns
220 -------
222 -------
221 An iterator over the desired lines. Each line is a 3-tuple, either
223 An iterator over the desired lines. Each line is a 3-tuple, either
222 (session, line, input) if output is False, or
224 (session, line, input) if output is False, or
223 (session, line, (input, output)) if output is True.
225 (session, line, (input, output)) if output is True.
224 """
226 """
225 if session == 0 or session==self.session_number: # Current session
227 if session == 0 or session==self.session_number: # Current session
226 return self._get_hist_session(start, stop, raw, output)
228 return self._get_hist_session(start, stop, raw, output)
227 if session < 0:
229 if session < 0:
228 session += self.session_number
230 session += self.session_number
229
231
230 if stop:
232 if stop:
231 lineclause = "line >= ? AND line < ?"
233 lineclause = "line >= ? AND line < ?"
232 params = (session, start, stop)
234 params = (session, start, stop)
233 else:
235 else:
234 lineclause = "line>=?"
236 lineclause = "line>=?"
235 params = (session, start)
237 params = (session, start)
236
238
237 return self._get_hist_sql("WHERE session==? AND %s""" % lineclause,
239 return self._get_hist_sql("WHERE session==? AND %s""" % lineclause,
238 params, raw=raw, output=output)
240 params, raw=raw, output=output)
239
241
240 def get_hist_from_rangestr(self, rangestr, raw=True, output=False):
242 def get_hist_from_rangestr(self, rangestr, raw=True, output=False):
241 """Get lines of history from a string of ranges, as used by magic
243 """Get lines of history from a string of ranges, as used by magic
242 commands %hist, %save, %macro, etc."""
244 commands %hist, %save, %macro, etc."""
243 for sess, s, e in extract_hist_ranges(rangestr):
245 for sess, s, e in extract_hist_ranges(rangestr):
244 for line in self.get_history(sess, s, e, raw=raw, output=output):
246 for line in self.get_history(sess, s, e, raw=raw, output=output):
245 yield line
247 yield line
246
248
247 def store_inputs(self, line_num, source, source_raw=None):
249 def store_inputs(self, line_num, source, source_raw=None):
248 """Store source and raw input in history and create input cache
250 """Store source and raw input in history and create input cache
249 variables _i*.
251 variables _i*.
250
252
251 Parameters
253 Parameters
252 ----------
254 ----------
253 line_num : int
255 line_num : int
254 The prompt number of this input.
256 The prompt number of this input.
255
257
256 source : str
258 source : str
257 Python input.
259 Python input.
258
260
259 source_raw : str, optional
261 source_raw : str, optional
260 If given, this is the raw input without any IPython transformations
262 If given, this is the raw input without any IPython transformations
261 applied to it. If not given, ``source`` is used.
263 applied to it. If not given, ``source`` is used.
262 """
264 """
263 if source_raw is None:
265 if source_raw is None:
264 source_raw = source
266 source_raw = source
265
267
266 # do not store exit/quit commands
268 # do not store exit/quit commands
267 if source_raw.strip() in self._exit_commands:
269 if source_raw.strip() in self._exit_commands:
268 return
270 return
269
271
270 self.input_hist_parsed.append(source.rstrip())
272 self.input_hist_parsed.append(source.rstrip())
271 self.input_hist_raw.append(source_raw.rstrip())
273 self.input_hist_raw.append(source_raw.rstrip())
272
274
273 self.db_input_cache.append((self.session_number, line_num,
275 self.db_input_cache.append((self.session_number, line_num,
274 source, source_raw))
276 source, source_raw))
275 # Trigger to flush cache and write to DB.
277 # Trigger to flush cache and write to DB.
276 if len(self.db_input_cache) >= self.db_cache_size:
278 if len(self.db_input_cache) >= self.db_cache_size:
277 self.writeout_cache()
279 self.writeout_cache()
278
280
279 # update the auto _i variables
281 # update the auto _i variables
280 self._iii = self._ii
282 self._iii = self._ii
281 self._ii = self._i
283 self._ii = self._i
282 self._i = self._i00
284 self._i = self._i00
283 self._i00 = source_raw
285 self._i00 = source_raw
284
286
285 # hackish access to user namespace to create _i1,_i2... dynamically
287 # hackish access to user namespace to create _i1,_i2... dynamically
286 new_i = '_i%s' % line_num
288 new_i = '_i%s' % line_num
287 to_main = {'_i': self._i,
289 to_main = {'_i': self._i,
288 '_ii': self._ii,
290 '_ii': self._ii,
289 '_iii': self._iii,
291 '_iii': self._iii,
290 new_i : self._i00 }
292 new_i : self._i00 }
291 self.shell.user_ns.update(to_main)
293 self.shell.user_ns.update(to_main)
292
294
293 def store_output(self, line_num, output):
295 def store_output(self, line_num, output):
294 if not self.db_log_output:
296 if not self.db_log_output:
295 return
297 return
296 db_row = (self.session_number, line_num, output)
298 db_row = (self.session_number, line_num, output)
297 if self.db_cache_size > 1:
299 if self.db_cache_size > 1:
298 self.db_output_cache.append(db_row)
300 self.db_output_cache.append(db_row)
299 else:
301 else:
300 with self.db:
302 with self.db:
301 self.db.execute("INSERT INTO output_history VALUES (?,?,?)", db_row)
303 self.db.execute("INSERT INTO output_history VALUES (?,?,?)", db_row)
302
304
303 def writeout_cache(self):
305 def writeout_cache(self):
304 #print(self.db_input_cache)
306 #print(self.db_input_cache)
305 with self.db:
307 with self.db:
306 self.db.executemany("INSERT INTO history VALUES (?, ?, ?, ?)",
308 self.db.executemany("INSERT INTO history VALUES (?, ?, ?, ?)",
307 self.db_input_cache)
309 self.db_input_cache)
308 self.db.executemany("INSERT INTO output_history VALUES (?, ?, ?)",
310 self.db.executemany("INSERT INTO output_history VALUES (?, ?, ?)",
309 self.db_output_cache)
311 self.db_output_cache)
310 self.db_input_cache = []
312 self.db_input_cache = []
311 self.db_output_cache = []
313 self.db_output_cache = []
312
314
313 def sync_inputs(self):
315 def sync_inputs(self):
314 """Ensure raw and translated histories have same length."""
316 """Ensure raw and translated histories have same length."""
315 lr = len(self.input_hist_raw)
317 lr = len(self.input_hist_raw)
316 lp = len(self.input_hist_parsed)
318 lp = len(self.input_hist_parsed)
317 if lp < lr:
319 if lp < lr:
318 self.input_hist_raw[:lr-lp] = []
320 self.input_hist_raw[:lr-lp] = []
319 elif lr < lp:
321 elif lr < lp:
320 self.input_hist_parsed[:lp-lr] = []
322 self.input_hist_parsed[:lp-lr] = []
321
323
322 def reset(self, new_session=True):
324 def reset(self, new_session=True):
323 """Clear the current session's history, and (optionally) start a new
325 """Clear the current session's history, and (optionally) start a new
324 session."""
326 session."""
325 self.input_hist_parsed[:] = [""]
327 self.input_hist_parsed[:] = [""]
326 self.input_hist_raw[:] = [""]
328 self.input_hist_raw[:] = [""]
327 self.output_hist.clear()
329 self.output_hist.clear()
328 # The directory history can't be completely empty
330 # The directory history can't be completely empty
329 self.dir_hist[:] = [os.getcwd()]
331 self.dir_hist[:] = [os.getcwd()]
330
332
331 if new_session:
333 if new_session:
332 self.writeout_cache()
334 self.writeout_cache()
333 self.init_db() # Get new session number
335 self.init_db() # Get new session number
334
336
335 # To match, e.g. ~5/8-~2/3
337 # To match, e.g. ~5/8-~2/3
336 range_re = re.compile(r"""
338 range_re = re.compile(r"""
337 ((?P<startsess>~?\d+)/)?
339 ((?P<startsess>~?\d+)/)?
338 (?P<start>\d+) # Only the start line num is compulsory
340 (?P<start>\d+) # Only the start line num is compulsory
339 ((?P<sep>[\-:])
341 ((?P<sep>[\-:])
340 ((?P<endsess>~?\d+)/)?
342 ((?P<endsess>~?\d+)/)?
341 (?P<end>\d+))?
343 (?P<end>\d+))?
342 """, re.VERBOSE)
344 """, re.VERBOSE)
343
345
344 def extract_hist_ranges(ranges_str):
346 def extract_hist_ranges(ranges_str):
345 """Turn a string of history ranges into 3-tuples of (session, start, stop).
347 """Turn a string of history ranges into 3-tuples of (session, start, stop).
346
348
347 Examples
349 Examples
348 --------
350 --------
349 list(extract_input_ranges("~8/5-~7/4 2"))
351 list(extract_input_ranges("~8/5-~7/4 2"))
350 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
352 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
351 """
353 """
352 for range_str in ranges_str.split():
354 for range_str in ranges_str.split():
353 rmatch = range_re.match(range_str)
355 rmatch = range_re.match(range_str)
354 start = int(rmatch.group("start"))
356 start = int(rmatch.group("start"))
355 end = rmatch.group("end")
357 end = rmatch.group("end")
356 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
358 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
357 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
359 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
358 end += 1
360 end += 1
359 startsess = rmatch.group("startsess") or "0"
361 startsess = rmatch.group("startsess") or "0"
360 endsess = rmatch.group("endsess") or startsess
362 endsess = rmatch.group("endsess") or startsess
361 startsess = int(startsess.replace("~","-"))
363 startsess = int(startsess.replace("~","-"))
362 endsess = int(endsess.replace("~","-"))
364 endsess = int(endsess.replace("~","-"))
363 assert endsess >= startsess
365 assert endsess >= startsess
364
366
365 if endsess == startsess:
367 if endsess == startsess:
366 yield (startsess, start, end)
368 yield (startsess, start, end)
367 continue
369 continue
368 # Multiple sessions in one range:
370 # Multiple sessions in one range:
369 yield (startsess, start, None)
371 yield (startsess, start, None)
370 for sess in range(startsess+1, endsess):
372 for sess in range(startsess+1, endsess):
371 yield (sess, 1, None)
373 yield (sess, 1, None)
372 yield (endsess, 1, end)
374 yield (endsess, 1, end)
373
375
374 def _format_lineno(session, line):
376 def _format_lineno(session, line):
375 """Helper function to format line numbers properly."""
377 """Helper function to format line numbers properly."""
376 if session == 0:
378 if session == 0:
377 return str(line)
379 return str(line)
378 return "%s#%s" % (session, line)
380 return "%s#%s" % (session, line)
379
381
380 @testdec.skip_doctest
382 @testdec.skip_doctest
381 def magic_history(self, parameter_s = ''):
383 def magic_history(self, parameter_s = ''):
382 """Print input history (_i<n> variables), with most recent last.
384 """Print input history (_i<n> variables), with most recent last.
383
385
384 %history -> print at most 40 inputs (some may be multi-line)\\
386 %history -> print at most 40 inputs (some may be multi-line)\\
385 %history n -> print at most n inputs\\
387 %history n -> print at most n inputs\\
386 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
388 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
387
389
388 By default, input history is printed without line numbers so it can be
390 By default, input history is printed without line numbers so it can be
389 directly pasted into an editor.
391 directly pasted into an editor.
390
392
391 With -n, each input's number <n> is shown, and is accessible as the
393 With -n, each input's number <n> is shown, and is accessible as the
392 automatically generated variable _i<n> as well as In[<n>]. Multi-line
394 automatically generated variable _i<n> as well as In[<n>]. Multi-line
393 statements are printed starting at a new line for easy copy/paste.
395 statements are printed starting at a new line for easy copy/paste.
394
396
395 Options:
397 Options:
396
398
397 -n: print line numbers for each input.
399 -n: print line numbers for each input.
398 This feature is only available if numbered prompts are in use.
400 This feature is only available if numbered prompts are in use.
399
401
400 -o: also print outputs for each input.
402 -o: also print outputs for each input.
401
403
402 -p: print classic '>>>' python prompts before each input. This is useful
404 -p: print classic '>>>' python prompts before each input. This is useful
403 for making documentation, and in conjunction with -o, for producing
405 for making documentation, and in conjunction with -o, for producing
404 doctest-ready output.
406 doctest-ready output.
405
407
406 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
408 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
407
409
408 -t: print the 'translated' history, as IPython understands it. IPython
410 -t: print the 'translated' history, as IPython understands it. IPython
409 filters your input and converts it all into valid Python source before
411 filters your input and converts it all into valid Python source before
410 executing it (things like magics or aliases are turned into function
412 executing it (things like magics or aliases are turned into function
411 calls, for example). With this option, you'll see the native history
413 calls, for example). With this option, you'll see the native history
412 instead of the user-entered version: '%cd /' will be seen as
414 instead of the user-entered version: '%cd /' will be seen as
413 'get_ipython().magic("%cd /")' instead of '%cd /'.
415 'get_ipython().magic("%cd /")' instead of '%cd /'.
414
416
415 -g: treat the arg as a pattern to grep for in (full) history.
417 -g: treat the arg as a pattern to grep for in (full) history.
416 This includes the saved history (almost all commands ever written).
418 This includes the saved history (almost all commands ever written).
417 Use '%hist -g' to show full saved history (may be very long).
419 Use '%hist -g' to show full saved history (may be very long).
418
420
419 -l: get the last n lines from all sessions. Specify n as a single arg, or
421 -l: get the last n lines from all sessions. Specify n as a single arg, or
420 the default is the last 10 lines.
422 the default is the last 10 lines.
421
423
422 -f FILENAME: instead of printing the output to the screen, redirect it to
424 -f FILENAME: instead of printing the output to the screen, redirect it to
423 the given file. The file is always overwritten, though IPython asks for
425 the given file. The file is always overwritten, though IPython asks for
424 confirmation first if it already exists.
426 confirmation first if it already exists.
425
427
426 Examples
428 Examples
427 --------
429 --------
428 ::
430 ::
429
431
430 In [6]: %hist -n 4 6
432 In [6]: %hist -n 4 6
431 4:a = 12
433 4:a = 12
432 5:print a**2
434 5:print a**2
433
435
434 """
436 """
435
437
436 if not self.shell.displayhook.do_full_cache:
438 if not self.shell.displayhook.do_full_cache:
437 print('This feature is only available if numbered prompts are in use.')
439 print('This feature is only available if numbered prompts are in use.')
438 return
440 return
439 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
441 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
440
442
441 # For brevity
443 # For brevity
442 history_manager = self.shell.history_manager
444 history_manager = self.shell.history_manager
443
445
444 def _format_lineno(session, line):
446 def _format_lineno(session, line):
445 """Helper function to format line numbers properly."""
447 """Helper function to format line numbers properly."""
446 if session in (0, history_manager.session_number):
448 if session in (0, history_manager.session_number):
447 return str(line)
449 return str(line)
448 return "%s/%s" % (session, line)
450 return "%s/%s" % (session, line)
449
451
450 # Check if output to specific file was requested.
452 # Check if output to specific file was requested.
451 try:
453 try:
452 outfname = opts['f']
454 outfname = opts['f']
453 except KeyError:
455 except KeyError:
454 outfile = IPython.utils.io.Term.cout # default
456 outfile = IPython.utils.io.Term.cout # default
455 # We don't want to close stdout at the end!
457 # We don't want to close stdout at the end!
456 close_at_end = False
458 close_at_end = False
457 else:
459 else:
458 if os.path.exists(outfname):
460 if os.path.exists(outfname):
459 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
461 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
460 print('Aborting.')
462 print('Aborting.')
461 return
463 return
462
464
463 outfile = open(outfname,'w')
465 outfile = open(outfname,'w')
464 close_at_end = True
466 close_at_end = True
465
467
466 print_nums = 'n' in opts
468 print_nums = 'n' in opts
467 get_output = 'o' in opts
469 get_output = 'o' in opts
468 pyprompts = 'p' in opts
470 pyprompts = 'p' in opts
469 # Raw history is the default
471 # Raw history is the default
470 raw = not('t' in opts)
472 raw = not('t' in opts)
471
473
472 default_length = 40
474 default_length = 40
473 pattern = None
475 pattern = None
474
476
475 if 'g' in opts: # Glob search
477 if 'g' in opts: # Glob search
476 pattern = "*" + args + "*" if args else "*"
478 pattern = "*" + args + "*" if args else "*"
477 hist = history_manager.get_hist_search(pattern, raw=raw,
479 hist = history_manager.get_hist_search(pattern, raw=raw,
478 output=get_output)
480 output=get_output)
479 elif 'l' in opts: # Get 'tail'
481 elif 'l' in opts: # Get 'tail'
480 try:
482 try:
481 n = int(args)
483 n = int(args)
482 except ValueError, IndexError:
484 except ValueError, IndexError:
483 n = 10
485 n = 10
484 hist = history_manager.get_hist_tail(n, raw=raw, output=get_output)
486 hist = history_manager.get_hist_tail(n, raw=raw, output=get_output)
485 else:
487 else:
486 if args: # Get history by ranges
488 if args: # Get history by ranges
487 hist = history_manager.get_hist_from_rangestr(args, raw, get_output)
489 hist = history_manager.get_hist_from_rangestr(args, raw, get_output)
488 else: # Just get history for the current session
490 else: # Just get history for the current session
489 hist = history_manager.get_history(raw=raw, output=get_output)
491 hist = history_manager.get_history(raw=raw, output=get_output)
490
492
491 # We could be displaying the entire history, so let's not try to pull it
493 # We could be displaying the entire history, so let's not try to pull it
492 # into a list in memory. Anything that needs more space will just misalign.
494 # into a list in memory. Anything that needs more space will just misalign.
493 width = 4
495 width = 4
494
496
495 for session, lineno, inline in hist:
497 for session, lineno, inline in hist:
496 # Print user history with tabs expanded to 4 spaces. The GUI clients
498 # Print user history with tabs expanded to 4 spaces. The GUI clients
497 # use hard tabs for easier usability in auto-indented code, but we want
499 # use hard tabs for easier usability in auto-indented code, but we want
498 # to produce PEP-8 compliant history for safe pasting into an editor.
500 # to produce PEP-8 compliant history for safe pasting into an editor.
499 if get_output:
501 if get_output:
500 inline, output = inline
502 inline, output = inline
501 inline = inline.expandtabs(4).rstrip()
503 inline = inline.expandtabs(4).rstrip()
502
504
503 multiline = "\n" in inline
505 multiline = "\n" in inline
504 line_sep = '\n' if multiline else ' '
506 line_sep = '\n' if multiline else ' '
505 if print_nums:
507 if print_nums:
506 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
508 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
507 line_sep), file=outfile, end='')
509 line_sep), file=outfile, end='')
508 if pyprompts:
510 if pyprompts:
509 print(">>> ", end="", file=outfile)
511 print(">>> ", end="", file=outfile)
510 if multiline:
512 if multiline:
511 inline = "\n... ".join(inline.splitlines()) + "\n..."
513 inline = "\n... ".join(inline.splitlines()) + "\n..."
512 print(inline, file=outfile)
514 print(inline, file=outfile)
513 if get_output and output:
515 if get_output and output:
514 print(output, file=outfile)
516 print(output, file=outfile)
515
517
516 if close_at_end:
518 if close_at_end:
517 outfile.close()
519 outfile.close()
518
520
519 # %hist is an alternative name
521 # %hist is an alternative name
520 magic_hist = magic_history
522 magic_hist = magic_history
521
523
522
524
523 def rep_f(self, arg):
525 def rep_f(self, arg):
524 r""" Repeat a command, or get command to input line for editing
526 r""" Repeat a command, or get command to input line for editing
525
527
526 - %rep (no arguments):
528 - %rep (no arguments):
527
529
528 Place a string version of last computation result (stored in the special '_'
530 Place a string version of last computation result (stored in the special '_'
529 variable) to the next input prompt. Allows you to create elaborate command
531 variable) to the next input prompt. Allows you to create elaborate command
530 lines without using copy-paste::
532 lines without using copy-paste::
531
533
532 $ l = ["hei", "vaan"]
534 $ l = ["hei", "vaan"]
533 $ "".join(l)
535 $ "".join(l)
534 ==> heivaan
536 ==> heivaan
535 $ %rep
537 $ %rep
536 $ heivaan_ <== cursor blinking
538 $ heivaan_ <== cursor blinking
537
539
538 %rep 45
540 %rep 45
539
541
540 Place history line 45 to next input prompt. Use %hist to find out the
542 Place history line 45 to next input prompt. Use %hist to find out the
541 number.
543 number.
542
544
543 %rep 1-4 6-7 3
545 %rep 1-4 6-7 3
544
546
545 Repeat the specified lines immediately. Input slice syntax is the same as
547 Repeat the specified lines immediately. Input slice syntax is the same as
546 in %macro and %save.
548 in %macro and %save.
547
549
548 %rep foo
550 %rep foo
549
551
550 Place the most recent line that has the substring "foo" to next input.
552 Place the most recent line that has the substring "foo" to next input.
551 (e.g. 'svn ci -m foobar').
553 (e.g. 'svn ci -m foobar').
552 """
554 """
553
555
554 opts,args = self.parse_options(arg,'',mode='list')
556 opts,args = self.parse_options(arg,'',mode='list')
555 if not args:
557 if not args:
556 self.set_next_input(str(self.shell.user_ns["_"]))
558 self.set_next_input(str(self.shell.user_ns["_"]))
557 return
559 return
558
560
559 if len(args) == 1 and not '-' in args[0]:
561 if len(args) == 1 and not '-' in args[0]:
560 arg = args[0]
562 arg = args[0]
561 if len(arg) > 1 and arg.startswith('0'):
563 if len(arg) > 1 and arg.startswith('0'):
562 # get from shadow hist
564 # get from shadow hist
563 num = int(arg[1:])
565 num = int(arg[1:])
564 line = self.shell.shadowhist.get(num)
566 line = self.shell.shadowhist.get(num)
565 self.set_next_input(str(line))
567 self.set_next_input(str(line))
566 return
568 return
567 try:
569 try:
568 num = int(args[0])
570 num = int(args[0])
569 self.set_next_input(str(self.shell.input_hist_raw[num]).rstrip())
571 self.set_next_input(str(self.shell.input_hist_raw[num]).rstrip())
570 return
572 return
571 except ValueError:
573 except ValueError:
572 pass
574 pass
573
575
574 for h in reversed(self.shell.input_hist_raw):
576 for h in reversed(self.shell.input_hist_raw):
575 if 'rep' in h:
577 if 'rep' in h:
576 continue
578 continue
577 if fnmatch.fnmatch(h,'*' + arg + '*'):
579 if fnmatch.fnmatch(h,'*' + arg + '*'):
578 self.set_next_input(str(h).rstrip())
580 self.set_next_input(str(h).rstrip())
579 return
581 return
580
582
581 try:
583 try:
582 lines = self.extract_input_slices(args, True)
584 lines = self.extract_input_slices(args, True)
583 print("lines", lines)
585 print("lines", lines)
584 self.run_cell(lines)
586 self.run_cell(lines)
585 except ValueError:
587 except ValueError:
586 print("Not found in recent history:", args)
588 print("Not found in recent history:", args)
587
589
588
590
589 def init_ipython(ip):
591 def init_ipython(ip):
590 ip.define_magic("rep",rep_f)
592 ip.define_magic("rep",rep_f)
591 ip.define_magic("hist",magic_hist)
593 ip.define_magic("hist",magic_hist)
592 ip.define_magic("history",magic_history)
594 ip.define_magic("history",magic_history)
593
595
594 # XXX - ipy_completers are in quarantine, need to be updated to new apis
596 # XXX - ipy_completers are in quarantine, need to be updated to new apis
595 #import ipy_completers
597 #import ipy_completers
596 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
598 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
General Comments 0
You need to be logged in to leave comments. Login now