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