##// END OF EJS Templates
Set default session parameter for HistoryManager.get_range()
Thomas Kluyver -
Show More
@@ -1,883 +1,883 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 atexit
16 import atexit
17 import datetime
17 import datetime
18 import os
18 import os
19 import re
19 import re
20 import sqlite3
20 import sqlite3
21 import threading
21 import threading
22
22
23 # Our own packages
23 # Our own packages
24 from IPython.config.configurable import Configurable
24 from IPython.config.configurable import Configurable
25
25
26 from IPython.testing.skipdoctest import skip_doctest
26 from IPython.testing.skipdoctest import skip_doctest
27 from IPython.utils import io
27 from IPython.utils import io
28 from IPython.utils.traitlets import Bool, Dict, Instance, Int, CInt, List, Unicode
28 from IPython.utils.traitlets import Bool, Dict, Instance, Int, CInt, List, Unicode
29 from IPython.utils.warn import warn
29 from IPython.utils.warn import warn
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # Classes and functions
32 # Classes and functions
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34
34
35 class HistoryAccessor(Configurable):
35 class HistoryAccessor(Configurable):
36 """Access the history database without adding to it. For use by standalone
36 """Access the history database without adding to it. For use by standalone
37 history tools."""
37 history tools."""
38 # String holding the path to the history file
38 # String holding the path to the history file
39 hist_file = Unicode(config=True)
39 hist_file = Unicode(config=True)
40
40
41 # The SQLite database
41 # The SQLite database
42 db = Instance(sqlite3.Connection)
42 db = Instance(sqlite3.Connection)
43
43
44 def __init__(self, hist_file=u'', shell=None, config=None, **traits):
44 def __init__(self, hist_file=u'', shell=None, config=None, **traits):
45 """Create a new history accessor.
45 """Create a new history accessor.
46
46
47 hist_file must be given, either as an argument or through config.
47 hist_file must be given, either as an argument or through config.
48 """
48 """
49 # We need a pointer back to the shell for various tasks.
49 # We need a pointer back to the shell for various tasks.
50 super(HistoryAccessor, self).__init__(shell=shell, config=config,
50 super(HistoryAccessor, self).__init__(shell=shell, config=config,
51 hist_file=hist_file, **traits)
51 hist_file=hist_file, **traits)
52
52
53 if self.hist_file == u'':
53 if self.hist_file == u'':
54 # No one has set the hist_file, yet.
54 # No one has set the hist_file, yet.
55 self.hist_file = self._get_hist_file_name(hist_file)
55 self.hist_file = self._get_hist_file_name(hist_file)
56
56
57 try:
57 try:
58 self.init_db()
58 self.init_db()
59 except sqlite3.DatabaseError:
59 except sqlite3.DatabaseError:
60 if os.path.isfile(self.hist_file):
60 if os.path.isfile(self.hist_file):
61 # Try to move the file out of the way.
61 # Try to move the file out of the way.
62 newpath = os.path.join(self.shell.profile_dir.location, "hist-corrupt.sqlite")
62 newpath = os.path.join(self.shell.profile_dir.location, "hist-corrupt.sqlite")
63 os.rename(self.hist_file, newpath)
63 os.rename(self.hist_file, newpath)
64 print("ERROR! History file wasn't a valid SQLite database.",
64 print("ERROR! History file wasn't a valid SQLite database.",
65 "It was moved to %s" % newpath, "and a new file created.")
65 "It was moved to %s" % newpath, "and a new file created.")
66 self.init_db()
66 self.init_db()
67 else:
67 else:
68 # The hist_file is probably :memory: or something else.
68 # The hist_file is probably :memory: or something else.
69 raise
69 raise
70
70
71 def _get_hist_file_name(self, hist_file=None):
71 def _get_hist_file_name(self, hist_file=None):
72 "Override to produce a default history file name."
72 "Override to produce a default history file name."
73 raise NotImplementedError("No default history file")
73 raise NotImplementedError("No default history file")
74
74
75 def init_db(self):
75 def init_db(self):
76 """Connect to the database, and create tables if necessary."""
76 """Connect to the database, and create tables if necessary."""
77 # use detect_types so that timestamps return datetime objects
77 # use detect_types so that timestamps return datetime objects
78 self.db = sqlite3.connect(self.hist_file, detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
78 self.db = sqlite3.connect(self.hist_file, detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
79 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
79 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
80 primary key autoincrement, start timestamp,
80 primary key autoincrement, start timestamp,
81 end timestamp, num_cmds integer, remark text)""")
81 end timestamp, num_cmds integer, remark text)""")
82 self.db.execute("""CREATE TABLE IF NOT EXISTS history
82 self.db.execute("""CREATE TABLE IF NOT EXISTS history
83 (session integer, line integer, source text, source_raw text,
83 (session integer, line integer, source text, source_raw text,
84 PRIMARY KEY (session, line))""")
84 PRIMARY KEY (session, line))""")
85 # Output history is optional, but ensure the table's there so it can be
85 # Output history is optional, but ensure the table's there so it can be
86 # enabled later.
86 # enabled later.
87 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
87 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
88 (session integer, line integer, output text,
88 (session integer, line integer, output text,
89 PRIMARY KEY (session, line))""")
89 PRIMARY KEY (session, line))""")
90 self.db.commit()
90 self.db.commit()
91
91
92 def writeout_cache(self):
92 def writeout_cache(self):
93 """Overridden by HistoryManager to dump the cache before certain
93 """Overridden by HistoryManager to dump the cache before certain
94 database lookups."""
94 database lookups."""
95 pass
95 pass
96
96
97 ## -------------------------------
97 ## -------------------------------
98 ## Methods for retrieving history:
98 ## Methods for retrieving history:
99 ## -------------------------------
99 ## -------------------------------
100 def _run_sql(self, sql, params, raw=True, output=False):
100 def _run_sql(self, sql, params, raw=True, output=False):
101 """Prepares and runs an SQL query for the history database.
101 """Prepares and runs an SQL query for the history database.
102
102
103 Parameters
103 Parameters
104 ----------
104 ----------
105 sql : str
105 sql : str
106 Any filtering expressions to go after SELECT ... FROM ...
106 Any filtering expressions to go after SELECT ... FROM ...
107 params : tuple
107 params : tuple
108 Parameters passed to the SQL query (to replace "?")
108 Parameters passed to the SQL query (to replace "?")
109 raw, output : bool
109 raw, output : bool
110 See :meth:`get_range`
110 See :meth:`get_range`
111
111
112 Returns
112 Returns
113 -------
113 -------
114 Tuples as :meth:`get_range`
114 Tuples as :meth:`get_range`
115 """
115 """
116 toget = 'source_raw' if raw else 'source'
116 toget = 'source_raw' if raw else 'source'
117 sqlfrom = "history"
117 sqlfrom = "history"
118 if output:
118 if output:
119 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
119 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
120 toget = "history.%s, output_history.output" % toget
120 toget = "history.%s, output_history.output" % toget
121 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
121 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
122 (toget, sqlfrom) + sql, params)
122 (toget, sqlfrom) + sql, params)
123 if output: # Regroup into 3-tuples, and parse JSON
123 if output: # Regroup into 3-tuples, and parse JSON
124 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
124 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
125 return cur
125 return cur
126
126
127 def get_session_info(self, session=0):
127 def get_session_info(self, session=0):
128 """get info about a session
128 """get info about a session
129
129
130 Parameters
130 Parameters
131 ----------
131 ----------
132
132
133 session : int
133 session : int
134 Session number to retrieve. The current session is 0, and negative
134 Session number to retrieve. The current session is 0, and negative
135 numbers count back from current session, so -1 is previous session.
135 numbers count back from current session, so -1 is previous session.
136
136
137 Returns
137 Returns
138 -------
138 -------
139
139
140 (session_id [int], start [datetime], end [datetime], num_cmds [int], remark [unicode])
140 (session_id [int], start [datetime], end [datetime], num_cmds [int], remark [unicode])
141
141
142 Sessions that are running or did not exit cleanly will have `end=None`
142 Sessions that are running or did not exit cleanly will have `end=None`
143 and `num_cmds=None`.
143 and `num_cmds=None`.
144
144
145 """
145 """
146
146
147 if session <= 0:
147 if session <= 0:
148 session += self.session_number
148 session += self.session_number
149
149
150 query = "SELECT * from sessions where session == ?"
150 query = "SELECT * from sessions where session == ?"
151 return self.db.execute(query, (session,)).fetchone()
151 return self.db.execute(query, (session,)).fetchone()
152
152
153 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
153 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
154 """Get the last n lines from the history database.
154 """Get the last n lines from the history database.
155
155
156 Parameters
156 Parameters
157 ----------
157 ----------
158 n : int
158 n : int
159 The number of lines to get
159 The number of lines to get
160 raw, output : bool
160 raw, output : bool
161 See :meth:`get_range`
161 See :meth:`get_range`
162 include_latest : bool
162 include_latest : bool
163 If False (default), n+1 lines are fetched, and the latest one
163 If False (default), n+1 lines are fetched, and the latest one
164 is discarded. This is intended to be used where the function
164 is discarded. This is intended to be used where the function
165 is called by a user command, which it should not return.
165 is called by a user command, which it should not return.
166
166
167 Returns
167 Returns
168 -------
168 -------
169 Tuples as :meth:`get_range`
169 Tuples as :meth:`get_range`
170 """
170 """
171 self.writeout_cache()
171 self.writeout_cache()
172 if not include_latest:
172 if not include_latest:
173 n += 1
173 n += 1
174 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
174 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
175 (n,), raw=raw, output=output)
175 (n,), raw=raw, output=output)
176 if not include_latest:
176 if not include_latest:
177 return reversed(list(cur)[1:])
177 return reversed(list(cur)[1:])
178 return reversed(list(cur))
178 return reversed(list(cur))
179
179
180 def search(self, pattern="*", raw=True, search_raw=True,
180 def search(self, pattern="*", raw=True, search_raw=True,
181 output=False):
181 output=False):
182 """Search the database using unix glob-style matching (wildcards
182 """Search the database using unix glob-style matching (wildcards
183 * and ?).
183 * and ?).
184
184
185 Parameters
185 Parameters
186 ----------
186 ----------
187 pattern : str
187 pattern : str
188 The wildcarded pattern to match when searching
188 The wildcarded pattern to match when searching
189 search_raw : bool
189 search_raw : bool
190 If True, search the raw input, otherwise, the parsed input
190 If True, search the raw input, otherwise, the parsed input
191 raw, output : bool
191 raw, output : bool
192 See :meth:`get_range`
192 See :meth:`get_range`
193
193
194 Returns
194 Returns
195 -------
195 -------
196 Tuples as :meth:`get_range`
196 Tuples as :meth:`get_range`
197 """
197 """
198 tosearch = "source_raw" if search_raw else "source"
198 tosearch = "source_raw" if search_raw else "source"
199 if output:
199 if output:
200 tosearch = "history." + tosearch
200 tosearch = "history." + tosearch
201 self.writeout_cache()
201 self.writeout_cache()
202 return self._run_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
202 return self._run_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
203 raw=raw, output=output)
203 raw=raw, output=output)
204
204
205 def get_range(self, session, start=1, stop=None, raw=True,output=False):
205 def get_range(self, session, start=1, stop=None, raw=True,output=False):
206 """Retrieve input by session.
206 """Retrieve input by session.
207
207
208 Parameters
208 Parameters
209 ----------
209 ----------
210 session : int
210 session : int
211 Session number to retrieve.
211 Session number to retrieve.
212 start : int
212 start : int
213 First line to retrieve.
213 First line to retrieve.
214 stop : int
214 stop : int
215 End of line range (excluded from output itself). If None, retrieve
215 End of line range (excluded from output itself). If None, retrieve
216 to the end of the session.
216 to the end of the session.
217 raw : bool
217 raw : bool
218 If True, return untranslated input
218 If True, return untranslated input
219 output : bool
219 output : bool
220 If True, attempt to include output. This will be 'real' Python
220 If True, attempt to include output. This will be 'real' Python
221 objects for the current session, or text reprs from previous
221 objects for the current session, or text reprs from previous
222 sessions if db_log_output was enabled at the time. Where no output
222 sessions if db_log_output was enabled at the time. Where no output
223 is found, None is used.
223 is found, None is used.
224
224
225 Returns
225 Returns
226 -------
226 -------
227 An iterator over the desired lines. Each line is a 3-tuple, either
227 An iterator over the desired lines. Each line is a 3-tuple, either
228 (session, line, input) if output is False, or
228 (session, line, input) if output is False, or
229 (session, line, (input, output)) if output is True.
229 (session, line, (input, output)) if output is True.
230 """
230 """
231 if stop:
231 if stop:
232 lineclause = "line >= ? AND line < ?"
232 lineclause = "line >= ? AND line < ?"
233 params = (session, start, stop)
233 params = (session, start, stop)
234 else:
234 else:
235 lineclause = "line>=?"
235 lineclause = "line>=?"
236 params = (session, start)
236 params = (session, start)
237
237
238 return self._run_sql("WHERE session==? AND %s""" % lineclause,
238 return self._run_sql("WHERE session==? AND %s""" % lineclause,
239 params, raw=raw, output=output)
239 params, raw=raw, output=output)
240
240
241 def get_range_by_str(self, rangestr, raw=True, output=False):
241 def get_range_by_str(self, rangestr, raw=True, output=False):
242 """Get lines of history from a string of ranges, as used by magic
242 """Get lines of history from a string of ranges, as used by magic
243 commands %hist, %save, %macro, etc.
243 commands %hist, %save, %macro, etc.
244
244
245 Parameters
245 Parameters
246 ----------
246 ----------
247 rangestr : str
247 rangestr : str
248 A string specifying ranges, e.g. "5 ~2/1-4". See
248 A string specifying ranges, e.g. "5 ~2/1-4". See
249 :func:`magic_history` for full details.
249 :func:`magic_history` for full details.
250 raw, output : bool
250 raw, output : bool
251 As :meth:`get_range`
251 As :meth:`get_range`
252
252
253 Returns
253 Returns
254 -------
254 -------
255 Tuples as :meth:`get_range`
255 Tuples as :meth:`get_range`
256 """
256 """
257 for sess, s, e in extract_hist_ranges(rangestr):
257 for sess, s, e in extract_hist_ranges(rangestr):
258 for line in self.get_range(sess, s, e, raw=raw, output=output):
258 for line in self.get_range(sess, s, e, raw=raw, output=output):
259 yield line
259 yield line
260
260
261
261
262 class HistoryManager(HistoryAccessor):
262 class HistoryManager(HistoryAccessor):
263 """A class to organize all history-related functionality in one place.
263 """A class to organize all history-related functionality in one place.
264 """
264 """
265 # Public interface
265 # Public interface
266
266
267 # An instance of the IPython shell we are attached to
267 # An instance of the IPython shell we are attached to
268 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
268 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
269 # Lists to hold processed and raw history. These start with a blank entry
269 # Lists to hold processed and raw history. These start with a blank entry
270 # so that we can index them starting from 1
270 # so that we can index them starting from 1
271 input_hist_parsed = List([""])
271 input_hist_parsed = List([""])
272 input_hist_raw = List([""])
272 input_hist_raw = List([""])
273 # A list of directories visited during session
273 # A list of directories visited during session
274 dir_hist = List()
274 dir_hist = List()
275 def _dir_hist_default(self):
275 def _dir_hist_default(self):
276 try:
276 try:
277 return [os.getcwdu()]
277 return [os.getcwdu()]
278 except OSError:
278 except OSError:
279 return []
279 return []
280
280
281 # A dict of output history, keyed with ints from the shell's
281 # A dict of output history, keyed with ints from the shell's
282 # execution count.
282 # execution count.
283 output_hist = Dict()
283 output_hist = Dict()
284 # The text/plain repr of outputs.
284 # The text/plain repr of outputs.
285 output_hist_reprs = Dict()
285 output_hist_reprs = Dict()
286
286
287 # The number of the current session in the history database
287 # The number of the current session in the history database
288 session_number = CInt()
288 session_number = CInt()
289 # Should we log output to the database? (default no)
289 # Should we log output to the database? (default no)
290 db_log_output = Bool(False, config=True)
290 db_log_output = Bool(False, config=True)
291 # Write to database every x commands (higher values save disk access & power)
291 # Write to database every x commands (higher values save disk access & power)
292 # Values of 1 or less effectively disable caching.
292 # Values of 1 or less effectively disable caching.
293 db_cache_size = Int(0, config=True)
293 db_cache_size = Int(0, config=True)
294 # The input and output caches
294 # The input and output caches
295 db_input_cache = List()
295 db_input_cache = List()
296 db_output_cache = List()
296 db_output_cache = List()
297
297
298 # History saving in separate thread
298 # History saving in separate thread
299 save_thread = Instance('IPython.core.history.HistorySavingThread')
299 save_thread = Instance('IPython.core.history.HistorySavingThread')
300 try: # Event is a function returning an instance of _Event...
300 try: # Event is a function returning an instance of _Event...
301 save_flag = Instance(threading._Event)
301 save_flag = Instance(threading._Event)
302 except AttributeError: # ...until Python 3.3, when it's a class.
302 except AttributeError: # ...until Python 3.3, when it's a class.
303 save_flag = Instance(threading.Event)
303 save_flag = Instance(threading.Event)
304
304
305 # Private interface
305 # Private interface
306 # Variables used to store the three last inputs from the user. On each new
306 # Variables used to store the three last inputs from the user. On each new
307 # history update, we populate the user's namespace with these, shifted as
307 # history update, we populate the user's namespace with these, shifted as
308 # necessary.
308 # necessary.
309 _i00 = Unicode(u'')
309 _i00 = Unicode(u'')
310 _i = Unicode(u'')
310 _i = Unicode(u'')
311 _ii = Unicode(u'')
311 _ii = Unicode(u'')
312 _iii = Unicode(u'')
312 _iii = Unicode(u'')
313
313
314 # A regex matching all forms of the exit command, so that we don't store
314 # A regex matching all forms of the exit command, so that we don't store
315 # them in the history (it's annoying to rewind the first entry and land on
315 # them in the history (it's annoying to rewind the first entry and land on
316 # an exit call).
316 # an exit call).
317 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
317 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
318
318
319 def __init__(self, shell=None, config=None, **traits):
319 def __init__(self, shell=None, config=None, **traits):
320 """Create a new history manager associated with a shell instance.
320 """Create a new history manager associated with a shell instance.
321 """
321 """
322 # We need a pointer back to the shell for various tasks.
322 # We need a pointer back to the shell for various tasks.
323 super(HistoryManager, self).__init__(shell=shell, config=config,
323 super(HistoryManager, self).__init__(shell=shell, config=config,
324 **traits)
324 **traits)
325 self.save_flag = threading.Event()
325 self.save_flag = threading.Event()
326 self.db_input_cache_lock = threading.Lock()
326 self.db_input_cache_lock = threading.Lock()
327 self.db_output_cache_lock = threading.Lock()
327 self.db_output_cache_lock = threading.Lock()
328 self.save_thread = HistorySavingThread(self)
328 self.save_thread = HistorySavingThread(self)
329 self.save_thread.start()
329 self.save_thread.start()
330
330
331 self.new_session()
331 self.new_session()
332
332
333 def _get_hist_file_name(self, hist_file=None):
333 def _get_hist_file_name(self, hist_file=None):
334 profile_dir = self.shell.profile_dir.location
334 profile_dir = self.shell.profile_dir.location
335 return os.path.join(profile_dir, 'history.sqlite')
335 return os.path.join(profile_dir, 'history.sqlite')
336
336
337 def new_session(self, conn=None):
337 def new_session(self, conn=None):
338 """Get a new session number."""
338 """Get a new session number."""
339 if conn is None:
339 if conn is None:
340 conn = self.db
340 conn = self.db
341
341
342 with conn:
342 with conn:
343 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
343 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
344 NULL, "") """, (datetime.datetime.now(),))
344 NULL, "") """, (datetime.datetime.now(),))
345 self.session_number = cur.lastrowid
345 self.session_number = cur.lastrowid
346
346
347 def end_session(self):
347 def end_session(self):
348 """Close the database session, filling in the end time and line count."""
348 """Close the database session, filling in the end time and line count."""
349 self.writeout_cache()
349 self.writeout_cache()
350 with self.db:
350 with self.db:
351 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
351 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
352 session==?""", (datetime.datetime.now(),
352 session==?""", (datetime.datetime.now(),
353 len(self.input_hist_parsed)-1, self.session_number))
353 len(self.input_hist_parsed)-1, self.session_number))
354 self.session_number = 0
354 self.session_number = 0
355
355
356 def name_session(self, name):
356 def name_session(self, name):
357 """Give the current session a name in the history database."""
357 """Give the current session a name in the history database."""
358 with self.db:
358 with self.db:
359 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
359 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
360 (name, self.session_number))
360 (name, self.session_number))
361
361
362 def reset(self, new_session=True):
362 def reset(self, new_session=True):
363 """Clear the session history, releasing all object references, and
363 """Clear the session history, releasing all object references, and
364 optionally open a new session."""
364 optionally open a new session."""
365 self.output_hist.clear()
365 self.output_hist.clear()
366 # The directory history can't be completely empty
366 # The directory history can't be completely empty
367 self.dir_hist[:] = [os.getcwdu()]
367 self.dir_hist[:] = [os.getcwdu()]
368
368
369 if new_session:
369 if new_session:
370 if self.session_number:
370 if self.session_number:
371 self.end_session()
371 self.end_session()
372 self.input_hist_parsed[:] = [""]
372 self.input_hist_parsed[:] = [""]
373 self.input_hist_raw[:] = [""]
373 self.input_hist_raw[:] = [""]
374 self.new_session()
374 self.new_session()
375
375
376 # ------------------------------
376 # ------------------------------
377 # Methods for retrieving history
377 # Methods for retrieving history
378 # ------------------------------
378 # ------------------------------
379 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
379 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
380 """Get input and output history from the current session. Called by
380 """Get input and output history from the current session. Called by
381 get_range, and takes similar parameters."""
381 get_range, and takes similar parameters."""
382 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
382 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
383
383
384 n = len(input_hist)
384 n = len(input_hist)
385 if start < 0:
385 if start < 0:
386 start += n
386 start += n
387 if not stop or (stop > n):
387 if not stop or (stop > n):
388 stop = n
388 stop = n
389 elif stop < 0:
389 elif stop < 0:
390 stop += n
390 stop += n
391
391
392 for i in range(start, stop):
392 for i in range(start, stop):
393 if output:
393 if output:
394 line = (input_hist[i], self.output_hist_reprs.get(i))
394 line = (input_hist[i], self.output_hist_reprs.get(i))
395 else:
395 else:
396 line = input_hist[i]
396 line = input_hist[i]
397 yield (0, i, line)
397 yield (0, i, line)
398
398
399 def get_range(self, session, start=1, stop=None, raw=True,output=False):
399 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
400 """Retrieve input by session.
400 """Retrieve input by session.
401
401
402 Parameters
402 Parameters
403 ----------
403 ----------
404 session : int
404 session : int
405 Session number to retrieve. The current session is 0, and negative
405 Session number to retrieve. The current session is 0, and negative
406 numbers count back from current session, so -1 is previous session.
406 numbers count back from current session, so -1 is previous session.
407 start : int
407 start : int
408 First line to retrieve.
408 First line to retrieve.
409 stop : int
409 stop : int
410 End of line range (excluded from output itself). If None, retrieve
410 End of line range (excluded from output itself). If None, retrieve
411 to the end of the session.
411 to the end of the session.
412 raw : bool
412 raw : bool
413 If True, return untranslated input
413 If True, return untranslated input
414 output : bool
414 output : bool
415 If True, attempt to include output. This will be 'real' Python
415 If True, attempt to include output. This will be 'real' Python
416 objects for the current session, or text reprs from previous
416 objects for the current session, or text reprs from previous
417 sessions if db_log_output was enabled at the time. Where no output
417 sessions if db_log_output was enabled at the time. Where no output
418 is found, None is used.
418 is found, None is used.
419
419
420 Returns
420 Returns
421 -------
421 -------
422 An iterator over the desired lines. Each line is a 3-tuple, either
422 An iterator over the desired lines. Each line is a 3-tuple, either
423 (session, line, input) if output is False, or
423 (session, line, input) if output is False, or
424 (session, line, (input, output)) if output is True.
424 (session, line, (input, output)) if output is True.
425 """
425 """
426 if session <= 0:
426 if session <= 0:
427 session += self.session_number
427 session += self.session_number
428 if session==self.session_number: # Current session
428 if session==self.session_number: # Current session
429 return self._get_range_session(start, stop, raw, output)
429 return self._get_range_session(start, stop, raw, output)
430 return super(HistoryManager, self).get_range(session, start, stop, raw, output)
430 return super(HistoryManager, self).get_range(session, start, stop, raw, output)
431
431
432 ## ----------------------------
432 ## ----------------------------
433 ## Methods for storing history:
433 ## Methods for storing history:
434 ## ----------------------------
434 ## ----------------------------
435 def store_inputs(self, line_num, source, source_raw=None):
435 def store_inputs(self, line_num, source, source_raw=None):
436 """Store source and raw input in history and create input cache
436 """Store source and raw input in history and create input cache
437 variables _i*.
437 variables _i*.
438
438
439 Parameters
439 Parameters
440 ----------
440 ----------
441 line_num : int
441 line_num : int
442 The prompt number of this input.
442 The prompt number of this input.
443
443
444 source : str
444 source : str
445 Python input.
445 Python input.
446
446
447 source_raw : str, optional
447 source_raw : str, optional
448 If given, this is the raw input without any IPython transformations
448 If given, this is the raw input without any IPython transformations
449 applied to it. If not given, ``source`` is used.
449 applied to it. If not given, ``source`` is used.
450 """
450 """
451 if source_raw is None:
451 if source_raw is None:
452 source_raw = source
452 source_raw = source
453 source = source.rstrip('\n')
453 source = source.rstrip('\n')
454 source_raw = source_raw.rstrip('\n')
454 source_raw = source_raw.rstrip('\n')
455
455
456 # do not store exit/quit commands
456 # do not store exit/quit commands
457 if self._exit_re.match(source_raw.strip()):
457 if self._exit_re.match(source_raw.strip()):
458 return
458 return
459
459
460 self.input_hist_parsed.append(source)
460 self.input_hist_parsed.append(source)
461 self.input_hist_raw.append(source_raw)
461 self.input_hist_raw.append(source_raw)
462
462
463 with self.db_input_cache_lock:
463 with self.db_input_cache_lock:
464 self.db_input_cache.append((line_num, source, source_raw))
464 self.db_input_cache.append((line_num, source, source_raw))
465 # Trigger to flush cache and write to DB.
465 # Trigger to flush cache and write to DB.
466 if len(self.db_input_cache) >= self.db_cache_size:
466 if len(self.db_input_cache) >= self.db_cache_size:
467 self.save_flag.set()
467 self.save_flag.set()
468
468
469 # update the auto _i variables
469 # update the auto _i variables
470 self._iii = self._ii
470 self._iii = self._ii
471 self._ii = self._i
471 self._ii = self._i
472 self._i = self._i00
472 self._i = self._i00
473 self._i00 = source_raw
473 self._i00 = source_raw
474
474
475 # hackish access to user namespace to create _i1,_i2... dynamically
475 # hackish access to user namespace to create _i1,_i2... dynamically
476 new_i = '_i%s' % line_num
476 new_i = '_i%s' % line_num
477 to_main = {'_i': self._i,
477 to_main = {'_i': self._i,
478 '_ii': self._ii,
478 '_ii': self._ii,
479 '_iii': self._iii,
479 '_iii': self._iii,
480 new_i : self._i00 }
480 new_i : self._i00 }
481 self.shell.user_ns.update(to_main)
481 self.shell.user_ns.update(to_main)
482
482
483 def store_output(self, line_num):
483 def store_output(self, line_num):
484 """If database output logging is enabled, this saves all the
484 """If database output logging is enabled, this saves all the
485 outputs from the indicated prompt number to the database. It's
485 outputs from the indicated prompt number to the database. It's
486 called by run_cell after code has been executed.
486 called by run_cell after code has been executed.
487
487
488 Parameters
488 Parameters
489 ----------
489 ----------
490 line_num : int
490 line_num : int
491 The line number from which to save outputs
491 The line number from which to save outputs
492 """
492 """
493 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
493 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
494 return
494 return
495 output = self.output_hist_reprs[line_num]
495 output = self.output_hist_reprs[line_num]
496
496
497 with self.db_output_cache_lock:
497 with self.db_output_cache_lock:
498 self.db_output_cache.append((line_num, output))
498 self.db_output_cache.append((line_num, output))
499 if self.db_cache_size <= 1:
499 if self.db_cache_size <= 1:
500 self.save_flag.set()
500 self.save_flag.set()
501
501
502 def _writeout_input_cache(self, conn):
502 def _writeout_input_cache(self, conn):
503 with conn:
503 with conn:
504 for line in self.db_input_cache:
504 for line in self.db_input_cache:
505 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
505 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
506 (self.session_number,)+line)
506 (self.session_number,)+line)
507
507
508 def _writeout_output_cache(self, conn):
508 def _writeout_output_cache(self, conn):
509 with conn:
509 with conn:
510 for line in self.db_output_cache:
510 for line in self.db_output_cache:
511 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
511 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
512 (self.session_number,)+line)
512 (self.session_number,)+line)
513
513
514 def writeout_cache(self, conn=None):
514 def writeout_cache(self, conn=None):
515 """Write any entries in the cache to the database."""
515 """Write any entries in the cache to the database."""
516 if conn is None:
516 if conn is None:
517 conn = self.db
517 conn = self.db
518
518
519 with self.db_input_cache_lock:
519 with self.db_input_cache_lock:
520 try:
520 try:
521 self._writeout_input_cache(conn)
521 self._writeout_input_cache(conn)
522 except sqlite3.IntegrityError:
522 except sqlite3.IntegrityError:
523 self.new_session(conn)
523 self.new_session(conn)
524 print("ERROR! Session/line number was not unique in",
524 print("ERROR! Session/line number was not unique in",
525 "database. History logging moved to new session",
525 "database. History logging moved to new session",
526 self.session_number)
526 self.session_number)
527 try: # Try writing to the new session. If this fails, don't recurse
527 try: # Try writing to the new session. If this fails, don't recurse
528 self._writeout_input_cache(conn)
528 self._writeout_input_cache(conn)
529 except sqlite3.IntegrityError:
529 except sqlite3.IntegrityError:
530 pass
530 pass
531 finally:
531 finally:
532 self.db_input_cache = []
532 self.db_input_cache = []
533
533
534 with self.db_output_cache_lock:
534 with self.db_output_cache_lock:
535 try:
535 try:
536 self._writeout_output_cache(conn)
536 self._writeout_output_cache(conn)
537 except sqlite3.IntegrityError:
537 except sqlite3.IntegrityError:
538 print("!! Session/line number for output was not unique",
538 print("!! Session/line number for output was not unique",
539 "in database. Output will not be stored.")
539 "in database. Output will not be stored.")
540 finally:
540 finally:
541 self.db_output_cache = []
541 self.db_output_cache = []
542
542
543
543
544 class HistorySavingThread(threading.Thread):
544 class HistorySavingThread(threading.Thread):
545 """This thread takes care of writing history to the database, so that
545 """This thread takes care of writing history to the database, so that
546 the UI isn't held up while that happens.
546 the UI isn't held up while that happens.
547
547
548 It waits for the HistoryManager's save_flag to be set, then writes out
548 It waits for the HistoryManager's save_flag to be set, then writes out
549 the history cache. The main thread is responsible for setting the flag when
549 the history cache. The main thread is responsible for setting the flag when
550 the cache size reaches a defined threshold."""
550 the cache size reaches a defined threshold."""
551 daemon = True
551 daemon = True
552 stop_now = False
552 stop_now = False
553 def __init__(self, history_manager):
553 def __init__(self, history_manager):
554 super(HistorySavingThread, self).__init__()
554 super(HistorySavingThread, self).__init__()
555 self.history_manager = history_manager
555 self.history_manager = history_manager
556 atexit.register(self.stop)
556 atexit.register(self.stop)
557
557
558 def run(self):
558 def run(self):
559 # We need a separate db connection per thread:
559 # We need a separate db connection per thread:
560 try:
560 try:
561 self.db = sqlite3.connect(self.history_manager.hist_file)
561 self.db = sqlite3.connect(self.history_manager.hist_file)
562 while True:
562 while True:
563 self.history_manager.save_flag.wait()
563 self.history_manager.save_flag.wait()
564 if self.stop_now:
564 if self.stop_now:
565 return
565 return
566 self.history_manager.save_flag.clear()
566 self.history_manager.save_flag.clear()
567 self.history_manager.writeout_cache(self.db)
567 self.history_manager.writeout_cache(self.db)
568 except Exception as e:
568 except Exception as e:
569 print(("The history saving thread hit an unexpected error (%s)."
569 print(("The history saving thread hit an unexpected error (%s)."
570 "History will not be written to the database.") % repr(e))
570 "History will not be written to the database.") % repr(e))
571
571
572 def stop(self):
572 def stop(self):
573 """This can be called from the main thread to safely stop this thread.
573 """This can be called from the main thread to safely stop this thread.
574
574
575 Note that it does not attempt to write out remaining history before
575 Note that it does not attempt to write out remaining history before
576 exiting. That should be done by calling the HistoryManager's
576 exiting. That should be done by calling the HistoryManager's
577 end_session method."""
577 end_session method."""
578 self.stop_now = True
578 self.stop_now = True
579 self.history_manager.save_flag.set()
579 self.history_manager.save_flag.set()
580 self.join()
580 self.join()
581
581
582
582
583 # To match, e.g. ~5/8-~2/3
583 # To match, e.g. ~5/8-~2/3
584 range_re = re.compile(r"""
584 range_re = re.compile(r"""
585 ((?P<startsess>~?\d+)/)?
585 ((?P<startsess>~?\d+)/)?
586 (?P<start>\d+) # Only the start line num is compulsory
586 (?P<start>\d+) # Only the start line num is compulsory
587 ((?P<sep>[\-:])
587 ((?P<sep>[\-:])
588 ((?P<endsess>~?\d+)/)?
588 ((?P<endsess>~?\d+)/)?
589 (?P<end>\d+))?
589 (?P<end>\d+))?
590 $""", re.VERBOSE)
590 $""", re.VERBOSE)
591
591
592 def extract_hist_ranges(ranges_str):
592 def extract_hist_ranges(ranges_str):
593 """Turn a string of history ranges into 3-tuples of (session, start, stop).
593 """Turn a string of history ranges into 3-tuples of (session, start, stop).
594
594
595 Examples
595 Examples
596 --------
596 --------
597 list(extract_input_ranges("~8/5-~7/4 2"))
597 list(extract_input_ranges("~8/5-~7/4 2"))
598 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
598 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
599 """
599 """
600 for range_str in ranges_str.split():
600 for range_str in ranges_str.split():
601 rmatch = range_re.match(range_str)
601 rmatch = range_re.match(range_str)
602 if not rmatch:
602 if not rmatch:
603 continue
603 continue
604 start = int(rmatch.group("start"))
604 start = int(rmatch.group("start"))
605 end = rmatch.group("end")
605 end = rmatch.group("end")
606 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
606 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
607 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
607 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
608 end += 1
608 end += 1
609 startsess = rmatch.group("startsess") or "0"
609 startsess = rmatch.group("startsess") or "0"
610 endsess = rmatch.group("endsess") or startsess
610 endsess = rmatch.group("endsess") or startsess
611 startsess = int(startsess.replace("~","-"))
611 startsess = int(startsess.replace("~","-"))
612 endsess = int(endsess.replace("~","-"))
612 endsess = int(endsess.replace("~","-"))
613 assert endsess >= startsess
613 assert endsess >= startsess
614
614
615 if endsess == startsess:
615 if endsess == startsess:
616 yield (startsess, start, end)
616 yield (startsess, start, end)
617 continue
617 continue
618 # Multiple sessions in one range:
618 # Multiple sessions in one range:
619 yield (startsess, start, None)
619 yield (startsess, start, None)
620 for sess in range(startsess+1, endsess):
620 for sess in range(startsess+1, endsess):
621 yield (sess, 1, None)
621 yield (sess, 1, None)
622 yield (endsess, 1, end)
622 yield (endsess, 1, end)
623
623
624 def _format_lineno(session, line):
624 def _format_lineno(session, line):
625 """Helper function to format line numbers properly."""
625 """Helper function to format line numbers properly."""
626 if session == 0:
626 if session == 0:
627 return str(line)
627 return str(line)
628 return "%s#%s" % (session, line)
628 return "%s#%s" % (session, line)
629
629
630 @skip_doctest
630 @skip_doctest
631 def magic_history(self, parameter_s = ''):
631 def magic_history(self, parameter_s = ''):
632 """Print input history (_i<n> variables), with most recent last.
632 """Print input history (_i<n> variables), with most recent last.
633
633
634 %history -> print at most 40 inputs (some may be multi-line)\\
634 %history -> print at most 40 inputs (some may be multi-line)\\
635 %history n -> print at most n inputs\\
635 %history n -> print at most n inputs\\
636 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
636 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
637
637
638 By default, input history is printed without line numbers so it can be
638 By default, input history is printed without line numbers so it can be
639 directly pasted into an editor. Use -n to show them.
639 directly pasted into an editor. Use -n to show them.
640
640
641 Ranges of history can be indicated using the syntax:
641 Ranges of history can be indicated using the syntax:
642 4 : Line 4, current session
642 4 : Line 4, current session
643 4-6 : Lines 4-6, current session
643 4-6 : Lines 4-6, current session
644 243/1-5: Lines 1-5, session 243
644 243/1-5: Lines 1-5, session 243
645 ~2/7 : Line 7, session 2 before current
645 ~2/7 : Line 7, session 2 before current
646 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
646 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
647 of 6 sessions ago.
647 of 6 sessions ago.
648 Multiple ranges can be entered, separated by spaces
648 Multiple ranges can be entered, separated by spaces
649
649
650 The same syntax is used by %macro, %save, %edit, %rerun
650 The same syntax is used by %macro, %save, %edit, %rerun
651
651
652 Options:
652 Options:
653
653
654 -n: print line numbers for each input.
654 -n: print line numbers for each input.
655 This feature is only available if numbered prompts are in use.
655 This feature is only available if numbered prompts are in use.
656
656
657 -o: also print outputs for each input.
657 -o: also print outputs for each input.
658
658
659 -p: print classic '>>>' python prompts before each input. This is useful
659 -p: print classic '>>>' python prompts before each input. This is useful
660 for making documentation, and in conjunction with -o, for producing
660 for making documentation, and in conjunction with -o, for producing
661 doctest-ready output.
661 doctest-ready output.
662
662
663 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
663 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
664
664
665 -t: print the 'translated' history, as IPython understands it. IPython
665 -t: print the 'translated' history, as IPython understands it. IPython
666 filters your input and converts it all into valid Python source before
666 filters your input and converts it all into valid Python source before
667 executing it (things like magics or aliases are turned into function
667 executing it (things like magics or aliases are turned into function
668 calls, for example). With this option, you'll see the native history
668 calls, for example). With this option, you'll see the native history
669 instead of the user-entered version: '%cd /' will be seen as
669 instead of the user-entered version: '%cd /' will be seen as
670 'get_ipython().magic("%cd /")' instead of '%cd /'.
670 'get_ipython().magic("%cd /")' instead of '%cd /'.
671
671
672 -g: treat the arg as a pattern to grep for in (full) history.
672 -g: treat the arg as a pattern to grep for in (full) history.
673 This includes the saved history (almost all commands ever written).
673 This includes the saved history (almost all commands ever written).
674 Use '%hist -g' to show full saved history (may be very long).
674 Use '%hist -g' to show full saved history (may be very long).
675
675
676 -l: get the last n lines from all sessions. Specify n as a single arg, or
676 -l: get the last n lines from all sessions. Specify n as a single arg, or
677 the default is the last 10 lines.
677 the default is the last 10 lines.
678
678
679 -f FILENAME: instead of printing the output to the screen, redirect it to
679 -f FILENAME: instead of printing the output to the screen, redirect it to
680 the given file. The file is always overwritten, though IPython asks for
680 the given file. The file is always overwritten, though IPython asks for
681 confirmation first if it already exists.
681 confirmation first if it already exists.
682
682
683 Examples
683 Examples
684 --------
684 --------
685 ::
685 ::
686
686
687 In [6]: %hist -n 4 6
687 In [6]: %hist -n 4 6
688 4:a = 12
688 4:a = 12
689 5:print a**2
689 5:print a**2
690
690
691 """
691 """
692
692
693 if not self.shell.displayhook.do_full_cache:
693 if not self.shell.displayhook.do_full_cache:
694 print('This feature is only available if numbered prompts are in use.')
694 print('This feature is only available if numbered prompts are in use.')
695 return
695 return
696 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
696 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
697
697
698 # For brevity
698 # For brevity
699 history_manager = self.shell.history_manager
699 history_manager = self.shell.history_manager
700
700
701 def _format_lineno(session, line):
701 def _format_lineno(session, line):
702 """Helper function to format line numbers properly."""
702 """Helper function to format line numbers properly."""
703 if session in (0, history_manager.session_number):
703 if session in (0, history_manager.session_number):
704 return str(line)
704 return str(line)
705 return "%s/%s" % (session, line)
705 return "%s/%s" % (session, line)
706
706
707 # Check if output to specific file was requested.
707 # Check if output to specific file was requested.
708 try:
708 try:
709 outfname = opts['f']
709 outfname = opts['f']
710 except KeyError:
710 except KeyError:
711 outfile = io.stdout # default
711 outfile = io.stdout # default
712 # We don't want to close stdout at the end!
712 # We don't want to close stdout at the end!
713 close_at_end = False
713 close_at_end = False
714 else:
714 else:
715 if os.path.exists(outfname):
715 if os.path.exists(outfname):
716 if not io.ask_yes_no("File %r exists. Overwrite?" % outfname):
716 if not io.ask_yes_no("File %r exists. Overwrite?" % outfname):
717 print('Aborting.')
717 print('Aborting.')
718 return
718 return
719
719
720 outfile = open(outfname,'w')
720 outfile = open(outfname,'w')
721 close_at_end = True
721 close_at_end = True
722
722
723 print_nums = 'n' in opts
723 print_nums = 'n' in opts
724 get_output = 'o' in opts
724 get_output = 'o' in opts
725 pyprompts = 'p' in opts
725 pyprompts = 'p' in opts
726 # Raw history is the default
726 # Raw history is the default
727 raw = not('t' in opts)
727 raw = not('t' in opts)
728
728
729 default_length = 40
729 default_length = 40
730 pattern = None
730 pattern = None
731
731
732 if 'g' in opts: # Glob search
732 if 'g' in opts: # Glob search
733 pattern = "*" + args + "*" if args else "*"
733 pattern = "*" + args + "*" if args else "*"
734 hist = history_manager.search(pattern, raw=raw, output=get_output)
734 hist = history_manager.search(pattern, raw=raw, output=get_output)
735 print_nums = True
735 print_nums = True
736 elif 'l' in opts: # Get 'tail'
736 elif 'l' in opts: # Get 'tail'
737 try:
737 try:
738 n = int(args)
738 n = int(args)
739 except ValueError, IndexError:
739 except ValueError, IndexError:
740 n = 10
740 n = 10
741 hist = history_manager.get_tail(n, raw=raw, output=get_output)
741 hist = history_manager.get_tail(n, raw=raw, output=get_output)
742 else:
742 else:
743 if args: # Get history by ranges
743 if args: # Get history by ranges
744 hist = history_manager.get_range_by_str(args, raw, get_output)
744 hist = history_manager.get_range_by_str(args, raw, get_output)
745 else: # Just get history for the current session
745 else: # Just get history for the current session
746 hist = history_manager.get_range(raw=raw, output=get_output)
746 hist = history_manager.get_range(raw=raw, output=get_output)
747
747
748 # We could be displaying the entire history, so let's not try to pull it
748 # We could be displaying the entire history, so let's not try to pull it
749 # into a list in memory. Anything that needs more space will just misalign.
749 # into a list in memory. Anything that needs more space will just misalign.
750 width = 4
750 width = 4
751
751
752 for session, lineno, inline in hist:
752 for session, lineno, inline in hist:
753 # Print user history with tabs expanded to 4 spaces. The GUI clients
753 # Print user history with tabs expanded to 4 spaces. The GUI clients
754 # use hard tabs for easier usability in auto-indented code, but we want
754 # use hard tabs for easier usability in auto-indented code, but we want
755 # to produce PEP-8 compliant history for safe pasting into an editor.
755 # to produce PEP-8 compliant history for safe pasting into an editor.
756 if get_output:
756 if get_output:
757 inline, output = inline
757 inline, output = inline
758 inline = inline.expandtabs(4).rstrip()
758 inline = inline.expandtabs(4).rstrip()
759
759
760 multiline = "\n" in inline
760 multiline = "\n" in inline
761 line_sep = '\n' if multiline else ' '
761 line_sep = '\n' if multiline else ' '
762 if print_nums:
762 if print_nums:
763 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
763 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
764 line_sep), file=outfile, end='')
764 line_sep), file=outfile, end='')
765 if pyprompts:
765 if pyprompts:
766 print(">>> ", end="", file=outfile)
766 print(">>> ", end="", file=outfile)
767 if multiline:
767 if multiline:
768 inline = "\n... ".join(inline.splitlines()) + "\n..."
768 inline = "\n... ".join(inline.splitlines()) + "\n..."
769 print(inline, file=outfile)
769 print(inline, file=outfile)
770 if get_output and output:
770 if get_output and output:
771 print(output, file=outfile)
771 print(output, file=outfile)
772
772
773 if close_at_end:
773 if close_at_end:
774 outfile.close()
774 outfile.close()
775
775
776
776
777 def magic_rep(self, arg):
777 def magic_rep(self, arg):
778 r"""Repeat a command, or get command to input line for editing. %recall and
778 r"""Repeat a command, or get command to input line for editing. %recall and
779 %rep are equivalent.
779 %rep are equivalent.
780
780
781 - %recall (no arguments):
781 - %recall (no arguments):
782
782
783 Place a string version of last computation result (stored in the special '_'
783 Place a string version of last computation result (stored in the special '_'
784 variable) to the next input prompt. Allows you to create elaborate command
784 variable) to the next input prompt. Allows you to create elaborate command
785 lines without using copy-paste::
785 lines without using copy-paste::
786
786
787 In[1]: l = ["hei", "vaan"]
787 In[1]: l = ["hei", "vaan"]
788 In[2]: "".join(l)
788 In[2]: "".join(l)
789 Out[2]: heivaan
789 Out[2]: heivaan
790 In[3]: %rep
790 In[3]: %rep
791 In[4]: heivaan_ <== cursor blinking
791 In[4]: heivaan_ <== cursor blinking
792
792
793 %recall 45
793 %recall 45
794
794
795 Place history line 45 on the next input prompt. Use %hist to find
795 Place history line 45 on the next input prompt. Use %hist to find
796 out the number.
796 out the number.
797
797
798 %recall 1-4
798 %recall 1-4
799
799
800 Combine the specified lines into one cell, and place it on the next
800 Combine the specified lines into one cell, and place it on the next
801 input prompt. See %history for the slice syntax.
801 input prompt. See %history for the slice syntax.
802
802
803 %recall foo+bar
803 %recall foo+bar
804
804
805 If foo+bar can be evaluated in the user namespace, the result is
805 If foo+bar can be evaluated in the user namespace, the result is
806 placed at the next input prompt. Otherwise, the history is searched
806 placed at the next input prompt. Otherwise, the history is searched
807 for lines which contain that substring, and the most recent one is
807 for lines which contain that substring, and the most recent one is
808 placed at the next input prompt.
808 placed at the next input prompt.
809 """
809 """
810 if not arg: # Last output
810 if not arg: # Last output
811 self.set_next_input(str(self.shell.user_ns["_"]))
811 self.set_next_input(str(self.shell.user_ns["_"]))
812 return
812 return
813 # Get history range
813 # Get history range
814 histlines = self.history_manager.get_range_by_str(arg)
814 histlines = self.history_manager.get_range_by_str(arg)
815 cmd = "\n".join(x[2] for x in histlines)
815 cmd = "\n".join(x[2] for x in histlines)
816 if cmd:
816 if cmd:
817 self.set_next_input(cmd.rstrip())
817 self.set_next_input(cmd.rstrip())
818 return
818 return
819
819
820 try: # Variable in user namespace
820 try: # Variable in user namespace
821 cmd = str(eval(arg, self.shell.user_ns))
821 cmd = str(eval(arg, self.shell.user_ns))
822 except Exception: # Search for term in history
822 except Exception: # Search for term in history
823 histlines = self.history_manager.search("*"+arg+"*")
823 histlines = self.history_manager.search("*"+arg+"*")
824 for h in reversed([x[2] for x in histlines]):
824 for h in reversed([x[2] for x in histlines]):
825 if 'rep' in h:
825 if 'rep' in h:
826 continue
826 continue
827 self.set_next_input(h.rstrip())
827 self.set_next_input(h.rstrip())
828 return
828 return
829 else:
829 else:
830 self.set_next_input(cmd.rstrip())
830 self.set_next_input(cmd.rstrip())
831 print("Couldn't evaluate or find in history:", arg)
831 print("Couldn't evaluate or find in history:", arg)
832
832
833 def magic_rerun(self, parameter_s=''):
833 def magic_rerun(self, parameter_s=''):
834 """Re-run previous input
834 """Re-run previous input
835
835
836 By default, you can specify ranges of input history to be repeated
836 By default, you can specify ranges of input history to be repeated
837 (as with %history). With no arguments, it will repeat the last line.
837 (as with %history). With no arguments, it will repeat the last line.
838
838
839 Options:
839 Options:
840
840
841 -l <n> : Repeat the last n lines of input, not including the
841 -l <n> : Repeat the last n lines of input, not including the
842 current command.
842 current command.
843
843
844 -g foo : Repeat the most recent line which contains foo
844 -g foo : Repeat the most recent line which contains foo
845 """
845 """
846 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
846 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
847 if "l" in opts: # Last n lines
847 if "l" in opts: # Last n lines
848 n = int(opts['l'])
848 n = int(opts['l'])
849 hist = self.history_manager.get_tail(n)
849 hist = self.history_manager.get_tail(n)
850 elif "g" in opts: # Search
850 elif "g" in opts: # Search
851 p = "*"+opts['g']+"*"
851 p = "*"+opts['g']+"*"
852 hist = list(self.history_manager.search(p))
852 hist = list(self.history_manager.search(p))
853 for l in reversed(hist):
853 for l in reversed(hist):
854 if "rerun" not in l[2]:
854 if "rerun" not in l[2]:
855 hist = [l] # The last match which isn't a %rerun
855 hist = [l] # The last match which isn't a %rerun
856 break
856 break
857 else:
857 else:
858 hist = [] # No matches except %rerun
858 hist = [] # No matches except %rerun
859 elif args: # Specify history ranges
859 elif args: # Specify history ranges
860 hist = self.history_manager.get_range_by_str(args)
860 hist = self.history_manager.get_range_by_str(args)
861 else: # Last line
861 else: # Last line
862 hist = self.history_manager.get_tail(1)
862 hist = self.history_manager.get_tail(1)
863 hist = [x[2] for x in hist]
863 hist = [x[2] for x in hist]
864 if not hist:
864 if not hist:
865 print("No lines in history match specification")
865 print("No lines in history match specification")
866 return
866 return
867 histlines = "\n".join(hist)
867 histlines = "\n".join(hist)
868 print("=== Executing: ===")
868 print("=== Executing: ===")
869 print(histlines)
869 print(histlines)
870 print("=== Output: ===")
870 print("=== Output: ===")
871 self.run_cell("\n".join(hist), store_history=False)
871 self.run_cell("\n".join(hist), store_history=False)
872
872
873
873
874 def init_ipython(ip):
874 def init_ipython(ip):
875 ip.define_magic("rep", magic_rep)
875 ip.define_magic("rep", magic_rep)
876 ip.define_magic("recall", magic_rep)
876 ip.define_magic("recall", magic_rep)
877 ip.define_magic("rerun", magic_rerun)
877 ip.define_magic("rerun", magic_rerun)
878 ip.define_magic("hist",magic_history) # Alternative name
878 ip.define_magic("hist",magic_history) # Alternative name
879 ip.define_magic("history",magic_history)
879 ip.define_magic("history",magic_history)
880
880
881 # XXX - ipy_completers are in quarantine, need to be updated to new apis
881 # XXX - ipy_completers are in quarantine, need to be updated to new apis
882 #import ipy_completers
882 #import ipy_completers
883 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
883 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
General Comments 0
You need to be logged in to leave comments. Login now