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