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