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