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