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