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