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