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