##// END OF EJS Templates
Merge branch 'sqlite-history' of github.com:takluyver/ipython
Thomas Kluyver -
r3439:d7ca41af merge
parent child Browse files
Show More
@@ -148,3 +148,14 b' c = get_config()'
148 148 # c.AliasManager.user_aliases = [
149 149 # ('foo', 'echo Hi')
150 150 # ]
151
152 #-----------------------------------------------------------------------------
153 # HistoryManager options
154 #-----------------------------------------------------------------------------
155
156 # Enable logging output as well as input to the database.
157 # c.HistoryManager.db_log_output = False
158
159 # Only write to the database every n commands - this can save disk
160 # access (and hence power) over the default of writing on every command.
161 # c.HistoryManager.db_cache_size = 0
@@ -277,10 +277,13 b' class DisplayHook(Configurable):'
277 277 self.shell.user_ns.update(to_main)
278 278 self.shell.user_ns['_oh'][self.prompt_count] = result
279 279
280 def log_output(self, result):
280 def log_output(self, format_dict):
281 281 """Log the output."""
282 282 if self.shell.logger.log_output:
283 self.shell.logger.log_write(repr(result), 'output')
283 self.shell.logger.log_write(format_dict['text/plain'], 'output')
284 # This is a defaultdict of lists, so we can always append
285 self.shell.history_manager.output_hist_reprs[self.prompt_count]\
286 .append(format_dict['text/plain'])
284 287
285 288 def finish_displayhook(self):
286 289 """Finish up all displayhook activities."""
@@ -300,7 +303,7 b' class DisplayHook(Configurable):'
300 303 format_dict = self.compute_format_data(result)
301 304 self.write_format_data(format_dict)
302 305 self.update_user_ns(result)
303 self.log_output(result)
306 self.log_output(format_dict)
304 307 self.finish_displayhook()
305 308
306 309 def flush(self):
This diff has been collapsed as it changes many lines, (863 lines changed) Show them Hide them
@@ -13,47 +13,61 b''
13 13 from __future__ import print_function
14 14
15 15 # Stdlib imports
16 import atexit
17 import fnmatch
16 import datetime
18 17 import json
19 18 import os
20 import sys
21 import threading
22 import time
19 import re
20 import sqlite3
21
22 from collections import defaultdict
23 23
24 24 # Our own packages
25 from IPython.config.configurable import Configurable
25 26 import IPython.utils.io
26 27
27 28 from IPython.testing import decorators as testdec
28 from IPython.utils.pickleshare import PickleShareDB
29 29 from IPython.utils.io import ask_yes_no
30 from IPython.utils.traitlets import Bool, Dict, Instance, Int, List, Unicode
30 31 from IPython.utils.warn import warn
31 32
32 33 #-----------------------------------------------------------------------------
33 34 # Classes and functions
34 35 #-----------------------------------------------------------------------------
35 36
36 class HistoryManager(object):
37 class HistoryManager(Configurable):
37 38 """A class to organize all history-related functionality in one place.
38 39 """
39 40 # Public interface
40 41
41 42 # An instance of the IPython shell we are attached to
42 shell = None
43 # A list to hold processed history
44 input_hist_parsed = None
45 # A list to hold raw history (as typed by user)
46 input_hist_raw = None
43 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
44 # Lists to hold processed and raw history. These start with a blank entry
45 # so that we can index them starting from 1
46 input_hist_parsed = List([""])
47 input_hist_raw = List([""])
47 48 # A list of directories visited during session
48 dir_hist = None
49 # A dict of output history, keyed with ints from the shell's execution count
50 output_hist = None
51 # String with path to the history file
52 hist_file = None
53 # PickleShareDB instance holding the raw data for the shadow history
54 shadow_db = None
55 # ShadowHist instance with the actual shadow history
56 shadow_hist = None
49 dir_hist = List()
50 # A dict of output history, keyed with ints from the shell's
51 # execution count. If there are several outputs from one command,
52 # only the last one is stored.
53 output_hist = Dict()
54 # Contains all outputs, in lists of reprs.
55 output_hist_reprs = Instance(defaultdict)
56
57 # String holding the path to the history file
58 hist_file = Unicode()
59 # The SQLite database
60 db = Instance(sqlite3.Connection)
61 # The number of the current session in the history database
62 session_number = Int()
63 # Should we log output to the database? (default no)
64 db_log_output = Bool(False, config=True)
65 # Write to database every x commands (higher values save disk access & power)
66 # Values of 1 or less effectively disable caching.
67 db_cache_size = Int(0, config=True)
68 # The input and output caches
69 db_input_cache = List()
70 db_output_cache = List()
57 71
58 72 # Private interface
59 73 # Variables used to store the three last inputs from the user. On each new
@@ -66,18 +80,11 b' class HistoryManager(object):'
66 80 # call).
67 81 _exit_commands = None
68 82
69 def __init__(self, shell):
83 def __init__(self, shell, config=None):
70 84 """Create a new history manager associated with a shell instance.
71 85 """
72 86 # We need a pointer back to the shell for various tasks.
73 self.shell = shell
74
75 # List of input with multi-line handling.
76 self.input_hist_parsed = []
77 # This one will hold the 'raw' input history, without any
78 # pre-processing. This will allow users to retrieve the input just as
79 # it was exactly typed in by the user, with %hist -r.
80 self.input_hist_raw = []
87 super(HistoryManager, self).__init__(shell=shell, config=config)
81 88
82 89 # list of visited directories
83 90 try:
@@ -85,149 +92,260 b' class HistoryManager(object):'
85 92 except OSError:
86 93 self.dir_hist = []
87 94
88 # dict of output history
89 self.output_hist = {}
90
91 95 # Now the history file
92 96 if shell.profile:
93 97 histfname = 'history-%s' % shell.profile
94 98 else:
95 99 histfname = 'history'
96 self.hist_file = os.path.join(shell.ipython_dir, histfname + '.json')
97
98 # Objects related to shadow history management
99 self._init_shadow_hist()
100 self.hist_file = os.path.join(shell.ipython_dir, histfname + '.sqlite')
101 try:
102 self.init_db()
103 except sqlite3.DatabaseError:
104 newpath = os.path.join(self.shell.ipython_dir, "hist-corrupt.sqlite")
105 os.rename(self.hist_file, newpath)
106 print("ERROR! History file wasn't a valid SQLite database.",
107 "It was moved to %s" % newpath, "and a new file created.")
108 self.init_db()
109
110 self.new_session()
100 111
101 112 self._i00, self._i, self._ii, self._iii = '','','',''
113 self.output_hist_reprs = defaultdict(list)
102 114
103 115 self._exit_commands = set(['Quit', 'quit', 'Exit', 'exit', '%Quit',
104 116 '%quit', '%Exit', '%exit'])
105
106 # Object is fully initialized, we can now call methods on it.
107 117
108 # Fill the history zero entry, user counter starts at 1
109 self.store_inputs('\n', '\n')
118 def init_db(self):
119 """Connect to the database, and create tables if necessary."""
120 self.db = sqlite3.connect(self.hist_file)
121 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
122 primary key autoincrement, start timestamp,
123 end timestamp, num_cmds integer, remark text)""")
124 self.db.execute("""CREATE TABLE IF NOT EXISTS history
125 (session integer, line integer, source text, source_raw text,
126 PRIMARY KEY (session, line))""")
127 # Output history is optional, but ensure the table's there so it can be
128 # enabled later.
129 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
130 (session integer, line integer, output text,
131 PRIMARY KEY (session, line))""")
132 self.db.commit()
133
134 def new_session(self):
135 """Get a new session number."""
136 with self.db:
137 cur = self.db.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
138 NULL, "") """, (datetime.datetime.now(),))
139 self.session_number = cur.lastrowid
140
141 def end_session(self):
142 """Close the database session, filling in the end time and line count."""
143 self.writeout_cache()
144 with self.db:
145 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
146 session==?""", (datetime.datetime.now(),
147 len(self.input_hist_parsed)-1, self.session_number))
148 self.session_number = 0
149
150 def name_session(self, name):
151 """Give the current session a name in the history database."""
152 with self.db:
153 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
154 (name, self.session_number))
155
156 def reset(self, new_session=True):
157 """Clear the session history, releasing all object references, and
158 optionally open a new session."""
159 if self.session_number:
160 self.end_session()
161 self.input_hist_parsed[:] = [""]
162 self.input_hist_raw[:] = [""]
163 self.output_hist.clear()
164 # The directory history can't be completely empty
165 self.dir_hist[:] = [os.getcwd()]
110 166
111 # Create and start the autosaver.
112 self.autosave_flag = threading.Event()
113 self.autosave_timer = HistorySaveThread(self.autosave_flag, 60)
114 self.autosave_timer.start()
115 # Register the autosave handler to be triggered as a post execute
116 # callback.
117 self.shell.register_post_execute(self.autosave_if_due)
118
119 def _init_shadow_hist(self):
120 try:
121 self.shadow_db = PickleShareDB(os.path.join(
122 self.shell.ipython_dir, 'db'))
123 except UnicodeDecodeError:
124 print("Your ipython_dir can't be decoded to unicode!")
125 print("Please set HOME environment variable to something that")
126 print(r"only has ASCII characters, e.g. c:\home")
127 print("Now it is", self.ipython_dir)
128 sys.exit()
129 self.shadow_hist = ShadowHist(self.shadow_db, self.shell)
167 if new_session:
168 self.new_session()
169
170 ## -------------------------------
171 ## Methods for retrieving history:
172 ## -------------------------------
173 def _run_sql(self, sql, params, raw=True, output=False):
174 """Prepares and runs an SQL query for the history database.
130 175
131 def populate_readline_history(self):
132 """Populate the readline history from the raw history.
133
134 We only store one copy of the raw history, which is persisted to a json
135 file on disk. The readline history is repopulated from the contents of
136 this file."""
137
138 try:
139 self.shell.readline.clear_history()
140 except AttributeError:
141 pass
142 else:
143 for h in self.input_hist_raw:
144 if not h.isspace():
145 for line in h.splitlines():
146 self.shell.readline.add_history(line)
147
148 def save_history(self):
149 """Save input history to a file (via readline library)."""
150 hist = dict(raw=self.input_hist_raw, #[-self.shell.history_length:],
151 parsed=self.input_hist_parsed) #[-self.shell.history_length:])
152 with open(self.hist_file,'wt') as hfile:
153 json.dump(hist, hfile,
154 sort_keys=True, indent=4)
155
156 def autosave_if_due(self):
157 """Check if the autosave event is set; if so, save history. We do it
158 this way so that the save takes place in the main thread."""
159 if self.autosave_flag.is_set():
160 self.save_history()
161 self.autosave_flag.clear()
176 Parameters
177 ----------
178 sql : str
179 Any filtering expressions to go after SELECT ... FROM ...
180 params : tuple
181 Parameters passed to the SQL query (to replace "?")
182 raw, output : bool
183 See :meth:`get_range`
162 184
163 def reload_history(self):
164 """Reload the input history from disk file."""
165
166 with open(self.hist_file,'rt') as hfile:
167 try:
168 hist = json.load(hfile)
169 except ValueError: # Ignore it if JSON is corrupt.
170 return
171 self.input_hist_parsed = hist['parsed']
172 self.input_hist_raw = hist['raw']
173 if self.shell.has_readline:
174 self.populate_readline_history()
185 Returns
186 -------
187 Tuples as :meth:`get_range`
188 """
189 toget = 'source_raw' if raw else 'source'
190 sqlfrom = "history"
191 if output:
192 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
193 toget = "history.%s, output_history.output" % toget
194 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
195 (toget, sqlfrom) + sql, params)
196 if output: # Regroup into 3-tuples, and parse JSON
197 loads = lambda out: json.loads(out) if out else None
198 return ((ses, lin, (inp, loads(out))) \
199 for ses, lin, inp, out in cur)
200 return cur
201
202
203 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
204 """Get the last n lines from the history database.
175 205
176 def get_history(self, index=None, raw=False, output=True):
177 """Get the history list.
178
179 Get the input and output history.
180
181 206 Parameters
182 207 ----------
183 index : n or (n1, n2) or None
184 If n, then the last entries. If a tuple, then all in
185 range(n1, n2). If None, then all entries. Raises IndexError if
186 the format of index is incorrect.
187 raw : bool
188 If True, return the raw input.
189 output : bool
190 If True, then return the output as well.
191
208 n : int
209 The number of lines to get
210 raw, output : bool
211 See :meth:`get_range`
212 include_latest : bool
213 If False (default), n+1 lines are fetched, and the latest one
214 is discarded. This is intended to be used where the function
215 is called by a user command, which it should not return.
216
192 217 Returns
193 218 -------
194 If output is True, then return a dict of tuples, keyed by the prompt
195 numbers and with values of (input, output). If output is False, then
196 a dict, keyed by the prompt number with the values of input. Raises
197 IndexError if no history is found.
219 Tuples as :meth:`get_range`
198 220 """
199 if raw:
200 input_hist = self.input_hist_raw
201 else:
202 input_hist = self.input_hist_parsed
221 self.writeout_cache()
222 if not include_latest:
223 n += 1
224 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
225 (n,), raw=raw, output=output)
226 if not include_latest:
227 return reversed(list(cur)[1:])
228 return reversed(list(cur))
229
230 def search(self, pattern="*", raw=True, search_raw=True,
231 output=False):
232 """Search the database using unix glob-style matching (wildcards
233 * and ?).
234
235 Parameters
236 ----------
237 pattern : str
238 The wildcarded pattern to match when searching
239 search_raw : bool
240 If True, search the raw input, otherwise, the parsed input
241 raw, output : bool
242 See :meth:`get_range`
243
244 Returns
245 -------
246 Tuples as :meth:`get_range`
247 """
248 tosearch = "source_raw" if search_raw else "source"
203 249 if output:
204 output_hist = self.output_hist
250 tosearch = "history." + tosearch
251 self.writeout_cache()
252 return self._run_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
253 raw=raw, output=output)
254
255 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
256 """Get input and output history from the current session. Called by
257 get_range, and takes similar parameters."""
258 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
259
205 260 n = len(input_hist)
206 if index is None:
207 start=0; stop=n
208 elif isinstance(index, int):
209 start=n-index; stop=n
210 elif isinstance(index, tuple) and len(index) == 2:
211 start=index[0]; stop=index[1]
212 else:
213 raise IndexError('Not a valid index for the input history: %r'
214 % index)
215 hist = {}
261 if start < 0:
262 start += n
263 if not stop:
264 stop = n
265 elif stop < 0:
266 stop += n
267
216 268 for i in range(start, stop):
217 269 if output:
218 hist[i] = (input_hist[i], output_hist.get(i))
270 line = (input_hist[i], self.output_hist_reprs.get(i))
219 271 else:
220 hist[i] = input_hist[i]
221 if not hist:
222 raise IndexError('No history for range of indices: %r' % index)
223 return hist
224
225 def store_inputs(self, source, source_raw=None):
272 line = input_hist[i]
273 yield (0, i, line)
274
275 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
276 """Retrieve input by session.
277
278 Parameters
279 ----------
280 session : int
281 Session number to retrieve. The current session is 0, and negative
282 numbers count back from current session, so -1 is previous session.
283 start : int
284 First line to retrieve.
285 stop : int
286 End of line range (excluded from output itself). If None, retrieve
287 to the end of the session.
288 raw : bool
289 If True, return untranslated input
290 output : bool
291 If True, attempt to include output. This will be 'real' Python
292 objects for the current session, or text reprs from previous
293 sessions if db_log_output was enabled at the time. Where no output
294 is found, None is used.
295
296 Returns
297 -------
298 An iterator over the desired lines. Each line is a 3-tuple, either
299 (session, line, input) if output is False, or
300 (session, line, (input, output)) if output is True.
301 """
302 if session == 0 or session==self.session_number: # Current session
303 return self._get_range_session(start, stop, raw, output)
304 if session < 0:
305 session += self.session_number
306
307 if stop:
308 lineclause = "line >= ? AND line < ?"
309 params = (session, start, stop)
310 else:
311 lineclause = "line>=?"
312 params = (session, start)
313
314 return self._run_sql("WHERE session==? AND %s""" % lineclause,
315 params, raw=raw, output=output)
316
317 def get_range_by_str(self, rangestr, raw=True, output=False):
318 """Get lines of history from a string of ranges, as used by magic
319 commands %hist, %save, %macro, etc.
320
321 Parameters
322 ----------
323 rangestr : str
324 A string specifying ranges, e.g. "5 ~2/1-4". See
325 :func:`magic_history` for full details.
326 raw, output : bool
327 As :meth:`get_range`
328
329 Returns
330 -------
331 Tuples as :meth:`get_range`
332 """
333 for sess, s, e in extract_hist_ranges(rangestr):
334 for line in self.get_range(sess, s, e, raw=raw, output=output):
335 yield line
336
337 ## ----------------------------
338 ## Methods for storing history:
339 ## ----------------------------
340 def store_inputs(self, line_num, source, source_raw=None):
226 341 """Store source and raw input in history and create input cache
227 342 variables _i*.
228 343
229 344 Parameters
230 345 ----------
346 line_num : int
347 The prompt number of this input.
348
231 349 source : str
232 350 Python input.
233 351
@@ -237,14 +355,20 b' class HistoryManager(object):'
237 355 """
238 356 if source_raw is None:
239 357 source_raw = source
358 source = source.rstrip('\n')
359 source_raw = source_raw.rstrip('\n')
240 360
241 361 # do not store exit/quit commands
242 362 if source_raw.strip() in self._exit_commands:
243 363 return
244 364
245 self.input_hist_parsed.append(source.rstrip())
246 self.input_hist_raw.append(source_raw.rstrip())
247 self.shadow_hist.add(source)
365 self.input_hist_parsed.append(source)
366 self.input_hist_raw.append(source_raw)
367
368 self.db_input_cache.append((line_num, source, source_raw))
369 # Trigger to flush cache and write to DB.
370 if len(self.db_input_cache) >= self.db_cache_size:
371 self.writeout_cache()
248 372
249 373 # update the auto _i variables
250 374 self._iii = self._ii
@@ -253,58 +377,114 b' class HistoryManager(object):'
253 377 self._i00 = source_raw
254 378
255 379 # hackish access to user namespace to create _i1,_i2... dynamically
256 new_i = '_i%s' % self.shell.execution_count
380 new_i = '_i%s' % line_num
257 381 to_main = {'_i': self._i,
258 382 '_ii': self._ii,
259 383 '_iii': self._iii,
260 384 new_i : self._i00 }
261 385 self.shell.user_ns.update(to_main)
262
263 def sync_inputs(self):
264 """Ensure raw and translated histories have same length."""
265 if len(self.input_hist_parsed) != len (self.input_hist_raw):
266 self.input_hist_raw[:] = self.input_hist_parsed
267
268 def reset(self):
269 """Clear all histories managed by this object."""
270 self.input_hist_parsed[:] = []
271 self.input_hist_raw[:] = []
272 self.output_hist.clear()
273 # The directory history can't be completely empty
274 self.dir_hist[:] = [os.getcwd()]
275
276 class HistorySaveThread(threading.Thread):
277 """This thread makes IPython save history periodically.
278
279 Without this class, IPython would only save the history on a clean exit.
280 This saves the history periodically (the current default is once per
281 minute), so that it is not lost in the event of a crash.
386
387 def store_output(self, line_num):
388 """If database output logging is enabled, this saves all the
389 outputs from the indicated prompt number to the database. It's
390 called by run_cell after code has been executed.
391
392 Parameters
393 ----------
394 line_num : int
395 The line number from which to save outputs
396 """
397 if (not self.db_log_output) or not self.output_hist_reprs[line_num]:
398 return
399 output = json.dumps(self.output_hist_reprs[line_num])
400
401 self.db_output_cache.append((line_num, output))
402 if self.db_cache_size <= 1:
403 self.writeout_cache()
404
405 def _writeout_input_cache(self):
406 for line in self.db_input_cache:
407 with self.db:
408 self.db.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
409 (self.session_number,)+line)
282 410
283 The implementation sets an event to indicate that history should be saved.
284 The actual save is carried out after executing a user command, to avoid
285 thread issues.
286 """
287 daemon = True
411 def _writeout_output_cache(self):
412 for line in self.db_output_cache:
413 with self.db:
414 self.db.execute("INSERT INTO output_history VALUES (?, ?, ?)",
415 (self.session_number,)+line)
288 416
289 def __init__(self, autosave_flag, time_interval=60):
290 threading.Thread.__init__(self)
291 self.time_interval = time_interval
292 self.autosave_flag = autosave_flag
293 self.exit_now = threading.Event()
294 # Ensure the thread is stopped tidily when exiting normally
295 atexit.register(self.stop)
296
297 def run(self):
298 while True:
299 self.exit_now.wait(self.time_interval)
300 if self.exit_now.is_set():
301 break
302 self.autosave_flag.set()
417 def writeout_cache(self):
418 """Write any entries in the cache to the database."""
419 try:
420 self._writeout_input_cache()
421 except sqlite3.IntegrityError:
422 self.new_session()
423 print("ERROR! Session/line number was not unique in",
424 "database. History logging moved to new session",
425 self.session_number)
426 try: # Try writing to the new session. If this fails, don't recurse
427 self.writeout_cache()
428 except sqlite3.IntegrityError:
429 pass
430 finally:
431 self.db_input_cache = []
303 432
304 def stop(self):
305 """Safely and quickly stop the autosave timer thread."""
306 self.exit_now.set()
307 self.join()
433 try:
434 self._writeout_output_cache()
435 except sqlite3.IntegrityError:
436 print("!! Session/line number for output was not unique",
437 "in database. Output will not be stored.")
438 finally:
439 self.db_output_cache = []
440
441
442 # To match, e.g. ~5/8-~2/3
443 range_re = re.compile(r"""
444 ((?P<startsess>~?\d+)/)?
445 (?P<start>\d+) # Only the start line num is compulsory
446 ((?P<sep>[\-:])
447 ((?P<endsess>~?\d+)/)?
448 (?P<end>\d+))?
449 """, re.VERBOSE)
450
451 def extract_hist_ranges(ranges_str):
452 """Turn a string of history ranges into 3-tuples of (session, start, stop).
453
454 Examples
455 --------
456 list(extract_input_ranges("~8/5-~7/4 2"))
457 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
458 """
459 for range_str in ranges_str.split():
460 rmatch = range_re.match(range_str)
461 if not rmatch:
462 continue
463 start = int(rmatch.group("start"))
464 end = rmatch.group("end")
465 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
466 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
467 end += 1
468 startsess = rmatch.group("startsess") or "0"
469 endsess = rmatch.group("endsess") or startsess
470 startsess = int(startsess.replace("~","-"))
471 endsess = int(endsess.replace("~","-"))
472 assert endsess >= startsess
473
474 if endsess == startsess:
475 yield (startsess, start, end)
476 continue
477 # Multiple sessions in one range:
478 yield (startsess, start, None)
479 for sess in range(startsess+1, endsess):
480 yield (sess, 1, None)
481 yield (endsess, 1, end)
482
483 def _format_lineno(session, line):
484 """Helper function to format line numbers properly."""
485 if session == 0:
486 return str(line)
487 return "%s#%s" % (session, line)
308 488
309 489 @testdec.skip_doctest
310 490 def magic_history(self, parameter_s = ''):
@@ -315,11 +495,18 b" def magic_history(self, parameter_s = ''):"
315 495 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
316 496
317 497 By default, input history is printed without line numbers so it can be
318 directly pasted into an editor.
319
320 With -n, each input's number <n> is shown, and is accessible as the
321 automatically generated variable _i<n> as well as In[<n>]. Multi-line
322 statements are printed starting at a new line for easy copy/paste.
498 directly pasted into an editor. Use -n to show them.
499
500 Ranges of history can be indicated using the syntax:
501 4 : Line 4, current session
502 4-6 : Lines 4-6, current session
503 243/1-5: Lines 1-5, session 243
504 ~2/7 : Line 7, session 2 before current
505 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
506 of 6 sessions ago.
507 Multiple ranges can be entered, separated by spaces
508
509 The same syntax is used by %macro, %save, %edit, %rerun
323 510
324 511 Options:
325 512
@@ -342,9 +529,11 b" def magic_history(self, parameter_s = ''):"
342 529 'get_ipython().magic("%cd /")' instead of '%cd /'.
343 530
344 531 -g: treat the arg as a pattern to grep for in (full) history.
345 This includes the "shadow history" (almost all commands ever written).
346 Use '%hist -g' to show full shadow history (may be very long).
347 In shadow history, every index nuwber starts with 0.
532 This includes the saved history (almost all commands ever written).
533 Use '%hist -g' to show full saved history (may be very long).
534
535 -l: get the last n lines from all sessions. Specify n as a single arg, or
536 the default is the last 10 lines.
348 537
349 538 -f FILENAME: instead of printing the output to the screen, redirect it to
350 539 the given file. The file is always overwritten, though IPython asks for
@@ -363,7 +552,16 b" def magic_history(self, parameter_s = ''):"
363 552 if not self.shell.displayhook.do_full_cache:
364 553 print('This feature is only available if numbered prompts are in use.')
365 554 return
366 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
555 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
556
557 # For brevity
558 history_manager = self.shell.history_manager
559
560 def _format_lineno(session, line):
561 """Helper function to format line numbers properly."""
562 if session in (0, history_manager.session_number):
563 return str(line)
564 return "%s/%s" % (session, line)
367 565
368 566 # Check if output to specific file was requested.
369 567 try:
@@ -380,96 +578,61 b" def magic_history(self, parameter_s = ''):"
380 578
381 579 outfile = open(outfname,'w')
382 580 close_at_end = True
383
384 if 't' in opts:
385 input_hist = self.shell.history_manager.input_hist_parsed
386 elif 'r' in opts:
387 input_hist = self.shell.history_manager.input_hist_raw
388 else:
389 # Raw history is the default
390 input_hist = self.shell.history_manager.input_hist_raw
391
392 default_length = 40
393 pattern = None
394 if 'g' in opts:
395 init = 1
396 final = len(input_hist)
397 parts = parameter_s.split(None, 1)
398 if len(parts) == 1:
399 parts += '*'
400 head, pattern = parts
401 pattern = "*" + pattern + "*"
402 elif len(args) == 0:
403 final = len(input_hist)-1
404 init = max(1,final-default_length)
405 elif len(args) == 1:
406 final = len(input_hist)
407 init = max(1, final-int(args[0]))
408 elif len(args) == 2:
409 init, final = map(int, args)
410 else:
411 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
412 print(self.magic_hist.__doc__, file=IPython.utils.io.Term.cout)
413 return
414 581
415 width = len(str(final))
416 line_sep = ['','\n']
417 582 print_nums = 'n' in opts
418 print_outputs = 'o' in opts
583 get_output = 'o' in opts
419 584 pyprompts = 'p' in opts
585 # Raw history is the default
586 raw = not('t' in opts)
587
588 default_length = 40
589 pattern = None
420 590
421 found = False
422 if pattern is not None:
423 sh = self.shell.history_manager.shadowhist.all()
424 for idx, s in sh:
425 if fnmatch.fnmatch(s, pattern):
426 print("0%d: %s" %(idx, s.expandtabs(4)), file=outfile)
427 found = True
591 if 'g' in opts: # Glob search
592 pattern = "*" + args + "*" if args else "*"
593 hist = history_manager.search(pattern, raw=raw, output=get_output)
594 elif 'l' in opts: # Get 'tail'
595 try:
596 n = int(args)
597 except ValueError, IndexError:
598 n = 10
599 hist = history_manager.get_tail(n, raw=raw, output=get_output)
600 else:
601 if args: # Get history by ranges
602 hist = history_manager.get_range_by_str(args, raw, get_output)
603 else: # Just get history for the current session
604 hist = history_manager.get_range(raw=raw, output=get_output)
428 605
429 if found:
430 print("===", file=outfile)
431 print("shadow history ends, fetch by %rep <number> (must start with 0)",
432 file=outfile)
433 print("=== start of normal history ===", file=outfile)
606 # We could be displaying the entire history, so let's not try to pull it
607 # into a list in memory. Anything that needs more space will just misalign.
608 width = 4
434 609
435 for in_num in range(init, final):
610 for session, lineno, inline in hist:
436 611 # Print user history with tabs expanded to 4 spaces. The GUI clients
437 612 # use hard tabs for easier usability in auto-indented code, but we want
438 613 # to produce PEP-8 compliant history for safe pasting into an editor.
439 inline = input_hist[in_num].expandtabs(4).rstrip()+'\n'
440
441 if pattern is not None and not fnmatch.fnmatch(inline, pattern):
442 continue
614 if get_output:
615 inline, output = inline
616 inline = inline.expandtabs(4).rstrip()
443 617
444 multiline = int(inline.count('\n') > 1)
618 multiline = "\n" in inline
619 line_sep = '\n' if multiline else ' '
445 620 if print_nums:
446 print('%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
447 file=outfile)
621 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
622 line_sep), file=outfile, end='')
448 623 if pyprompts:
449 print('>>>', file=outfile)
624 print(">>> ", end="", file=outfile)
450 625 if multiline:
451 lines = inline.splitlines()
452 print('\n... '.join(lines), file=outfile)
453 print('... ', file=outfile)
454 else:
455 print(inline, end='', file=outfile)
456 else:
457 print(inline, end='', file=outfile)
458 if print_outputs:
459 output = self.shell.history_manager.output_hist.get(in_num)
460 if output is not None:
461 print(repr(output), file=outfile)
626 inline = "\n... ".join(inline.splitlines()) + "\n..."
627 print(inline, file=outfile)
628 if get_output and output:
629 print("\n".join(output), file=outfile)
462 630
463 631 if close_at_end:
464 632 outfile.close()
465 633
466 634
467 def magic_hist(self, parameter_s=''):
468 """Alternate name for %history."""
469 return self.magic_history(parameter_s)
470
471
472 def rep_f(self, arg):
635 def magic_rep(self, arg):
473 636 r""" Repeat a command, or get command to input line for editing
474 637
475 638 - %rep (no arguments):
@@ -478,110 +641,98 b' def rep_f(self, arg):'
478 641 variable) to the next input prompt. Allows you to create elaborate command
479 642 lines without using copy-paste::
480 643
481 $ l = ["hei", "vaan"]
482 $ "".join(l)
483 ==> heivaan
484 $ %rep
485 $ heivaan_ <== cursor blinking
644 In[1]: l = ["hei", "vaan"]
645 In[2]: "".join(l)
646 Out[2]: heivaan
647 In[3]: %rep
648 In[4]: heivaan_ <== cursor blinking
486 649
487 650 %rep 45
488 651
489 Place history line 45 to next input prompt. Use %hist to find out the
490 number.
652 Place history line 45 on the next input prompt. Use %hist to find
653 out the number.
491 654
492 %rep 1-4 6-7 3
655 %rep 1-4
493 656
494 Repeat the specified lines immediately. Input slice syntax is the same as
495 in %macro and %save.
657 Combine the specified lines into one cell, and place it on the next
658 input prompt. See %history for the slice syntax.
496 659
497 %rep foo
660 %rep foo+bar
498 661
499 Place the most recent line that has the substring "foo" to next input.
500 (e.g. 'svn ci -m foobar').
662 If foo+bar can be evaluated in the user namespace, the result is
663 placed at the next input prompt. Otherwise, the history is searched
664 for lines which contain that substring, and the most recent one is
665 placed at the next input prompt.
501 666 """
502
503 opts,args = self.parse_options(arg,'',mode='list')
504 if not args:
667 if not arg: # Last output
505 668 self.set_next_input(str(self.shell.user_ns["_"]))
506 669 return
670 # Get history range
671 histlines = self.history_manager.get_range_by_str(arg)
672 cmd = "\n".join(x[2] for x in histlines)
673 if cmd:
674 self.set_next_input(cmd.rstrip())
675 return
507 676
508 if len(args) == 1 and not '-' in args[0]:
509 arg = args[0]
510 if len(arg) > 1 and arg.startswith('0'):
511 # get from shadow hist
512 num = int(arg[1:])
513 line = self.shell.shadowhist.get(num)
514 self.set_next_input(str(line))
515 return
516 try:
517 num = int(args[0])
518 self.set_next_input(str(self.shell.input_hist_raw[num]).rstrip())
519 return
520 except ValueError:
521 pass
522
523 for h in reversed(self.shell.input_hist_raw):
677 try: # Variable in user namespace
678 cmd = str(eval(arg, self.shell.user_ns))
679 except Exception: # Search for term in history
680 histlines = self.history_manager.search("*"+arg+"*")
681 for h in reversed([x[2] for x in histlines]):
524 682 if 'rep' in h:
525 683 continue
526 if fnmatch.fnmatch(h,'*' + arg + '*'):
527 self.set_next_input(str(h).rstrip())
528 return
529
530 try:
531 lines = self.extract_input_slices(args, True)
532 print("lines", lines)
533 self.run_cell(lines)
534 except ValueError:
535 print("Not found in recent history:", args)
684 self.set_next_input(h.rstrip())
685 return
686 else:
687 self.set_next_input(cmd.rstrip())
688 print("Couldn't evaluate or find in history:", arg)
536 689
537
538 _sentinel = object()
539
540 class ShadowHist(object):
541 def __init__(self, db, shell):
542 # cmd => idx mapping
543 self.curidx = 0
544 self.db = db
545 self.disabled = False
546 self.shell = shell
690 def magic_rerun(self, parameter_s=''):
691 """Re-run previous input
547 692
548 def inc_idx(self):
549 idx = self.db.get('shadowhist_idx', 1)
550 self.db['shadowhist_idx'] = idx + 1
551 return idx
552
553 def add(self, ent):
554 if self.disabled:
555 return
556 try:
557 old = self.db.hget('shadowhist', ent, _sentinel)
558 if old is not _sentinel:
559 return
560 newidx = self.inc_idx()
561 #print("new", newidx) # dbg
562 self.db.hset('shadowhist',ent, newidx)
563 except:
564 self.shell.showtraceback()
565 print("WARNING: disabling shadow history")
566 self.disabled = True
693 By default, you can specify ranges of input history to be repeated
694 (as with %history). With no arguments, it will repeat the last line.
567 695
568 def all(self):
569 d = self.db.hdict('shadowhist')
570 items = [(i,s) for (s,i) in d.iteritems()]
571 items.sort()
572 return items
573
574 def get(self, idx):
575 all = self.all()
576
577 for k, v in all:
578 if k == idx:
579 return v
696 Options:
697
698 -l <n> : Repeat the last n lines of input, not including the
699 current command.
700
701 -g foo : Repeat the most recent line which contains foo
702 """
703 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
704 if "l" in opts: # Last n lines
705 n = int(opts['l'])
706 hist = self.history_manager.get_tail(n)
707 elif "g" in opts: # Search
708 p = "*"+opts['g']+"*"
709 hist = list(self.history_manager.search(p))
710 for l in reversed(hist):
711 if "rerun" not in l[2]:
712 hist = [l] # The last match which isn't a %rerun
713 break
714 else:
715 hist = [] # No matches except %rerun
716 elif args: # Specify history ranges
717 hist = self.history_manager.get_range_by_str(args)
718 else: # Last line
719 hist = self.history_manager.get_tail(1)
720 hist = [x[2] for x in hist]
721 if not hist:
722 print("No lines in history match specification")
723 return
724 histlines = "\n".join(hist)
725 print("=== Executing: ===")
726 print(histlines)
727 print("=== Output: ===")
728 self.run_cell("\n".join(hist), store_history=False)
580 729
581 730
582 731 def init_ipython(ip):
583 ip.define_magic("rep",rep_f)
584 ip.define_magic("hist",magic_hist)
732 ip.define_magic("rep", magic_rep)
733 ip.define_magic("recall", magic_rep)
734 ip.define_magic("rerun", magic_rerun)
735 ip.define_magic("hist",magic_history) # Alternative name
585 736 ip.define_magic("history",magic_history)
586 737
587 738 # XXX - ipy_completers are in quarantine, need to be updated to new apis
@@ -56,11 +56,11 b' from IPython.core.prefilter import PrefilterManager, ESC_MAGIC'
56 56 from IPython.external.Itpl import ItplNS
57 57 from IPython.utils import PyColorize
58 58 from IPython.utils import io
59 from IPython.utils import pickleshare
60 59 from IPython.utils.doctestreload import doctest_reload
61 60 from IPython.utils.io import ask_yes_no, rprint
62 61 from IPython.utils.ipstruct import Struct
63 62 from IPython.utils.path import get_home_dir, get_ipython_dir, HomeDirError
63 from IPython.utils.pickleshare import PickleShareDB
64 64 from IPython.utils.process import system, getoutput
65 65 from IPython.utils.strdispatch import StrDispatch
66 66 from IPython.utils.syspathcontext import prepended_to_syspath
@@ -251,6 +251,11 b' class InteractiveShell(Configurable, Magic):'
251 251 # is what we want to do.
252 252 self.save_sys_module_state()
253 253 self.init_sys_modules()
254
255 # While we're trying to have each part of the code directly access what
256 # it needs without keeping redundant references to objects, we have too
257 # much legacy code that expects ip.db to exist.
258 self.db = PickleShareDB(os.path.join(self.ipython_dir, 'db'))
254 259
255 260 self.init_history()
256 261 self.init_encoding()
@@ -301,14 +306,6 b' class InteractiveShell(Configurable, Magic):'
301 306 self.hooks.late_startup_hook()
302 307 atexit.register(self.atexit_operations)
303 308
304 # While we're trying to have each part of the code directly access what it
305 # needs without keeping redundant references to objects, we have too much
306 # legacy code that expects ip.db to exist, so let's make it a property that
307 # retrieves the underlying object from our new history manager.
308 @property
309 def db(self):
310 return self.history_manager.shadow_db
311
312 309 @classmethod
313 310 def instance(cls, *args, **kwargs):
314 311 """Returns a global InteractiveShell instance."""
@@ -994,14 +991,16 b' class InteractiveShell(Configurable, Magic):'
994 991 # Finally, update the real user's namespace
995 992 self.user_ns.update(ns)
996 993
997 def reset(self):
994 def reset(self, new_session=True):
998 995 """Clear all internal namespaces.
999 996
1000 997 Note that this is much more aggressive than %reset, since it clears
1001 998 fully all namespaces, as well as all input/output lists.
999
1000 If new_session is True, a new history session will be opened.
1002 1001 """
1003 1002 # Clear histories
1004 self.history_manager.reset()
1003 self.history_manager.reset(new_session)
1005 1004
1006 1005 # Reset counter used to index all histories
1007 1006 self.execution_count = 0
@@ -1249,15 +1248,7 b' class InteractiveShell(Configurable, Magic):'
1249 1248
1250 1249 def init_history(self):
1251 1250 """Sets up the command history, and starts regular autosaves."""
1252 self.history_manager = HistoryManager(shell=self)
1253
1254 def save_history(self):
1255 """Save input history to a file (via readline library)."""
1256 self.history_manager.save_history()
1257
1258 def reload_history(self):
1259 """Reload the input history from disk file."""
1260 self.history_manager.reload_history()
1251 self.history_manager = HistoryManager(shell=self, config=self.config)
1261 1252
1262 1253 def history_saving_wrapper(self, func):
1263 1254 """ Wrap func for readline history saving
@@ -1278,9 +1269,6 b' class InteractiveShell(Configurable, Magic):'
1278 1269 self.reload_history()
1279 1270 return wrapper
1280 1271
1281 def get_history(self, index=None, raw=False, output=True):
1282 return self.history_manager.get_history(index, raw, output)
1283
1284 1272
1285 1273 #-------------------------------------------------------------------------
1286 1274 # Things related to exception handling and tracebacks (not debugging)
@@ -1561,11 +1549,13 b' class InteractiveShell(Configurable, Magic):'
1561 1549 readline.set_completer_delims(delims)
1562 1550 # otherwise we end up with a monster history after a while:
1563 1551 readline.set_history_length(self.history_length)
1564 try:
1565 #print '*** Reading readline history' # dbg
1566 self.reload_history()
1567 except IOError:
1568 pass # It doesn't exist yet.
1552
1553 # Load the last 1000 lines from history
1554 for _, _, cell in self.history_manager.get_tail(1000,
1555 include_latest=True):
1556 if cell.strip(): # Ignore blank lines
1557 for line in cell.splitlines():
1558 readline.add_history(line)
1569 1559
1570 1560 # Configure auto-indent for all platforms
1571 1561 self.set_autoindent(self.autoindent)
@@ -2071,14 +2061,16 b' class InteractiveShell(Configurable, Magic):'
2071 2061 self.showtraceback()
2072 2062 warn('Unknown failure executing file: <%s>' % fname)
2073 2063
2074 def run_cell(self, cell):
2075 """Run the contents of an entire multiline 'cell' of code.
2064 def run_cell(self, cell, store_history=True):
2065 """Run the contents of an entire multiline 'cell' of code, and store it
2066 in the history.
2076 2067
2077 2068 The cell is split into separate blocks which can be executed
2078 2069 individually. Then, based on how many blocks there are, they are
2079 2070 executed as follows:
2080 2071
2081 - A single block: 'single' mode.
2072 - A single block: 'single' mode. If it is also a single line, dynamic
2073 transformations, including automagic and macros, will be applied.
2082 2074
2083 2075 If there's more than one block, it depends:
2084 2076
@@ -2097,6 +2089,15 b' class InteractiveShell(Configurable, Magic):'
2097 2089 cell : str
2098 2090 A single or multiline string.
2099 2091 """
2092 # Store the untransformed code
2093 raw_cell = cell
2094
2095 # We only do dynamic transforms on a single line. We need to do this
2096 # first, because a macro can be expanded to several lines, which then
2097 # need to be split into blocks again.
2098 if len(cell.splitlines()) <= 1:
2099 temp = self.input_splitter.split_blocks(cell)
2100 cell = self.prefilter_manager.prefilter_line(temp[0])
2100 2101
2101 2102 # We need to break up the input into executable blocks that can be run
2102 2103 # in 'single' mode, to provide comfortable user behavior.
@@ -2108,32 +2109,36 b' class InteractiveShell(Configurable, Magic):'
2108 2109 # Store the 'ipython' version of the cell as well, since that's what
2109 2110 # needs to go into the translated history and get executed (the
2110 2111 # original cell may contain non-python syntax).
2111 ipy_cell = ''.join(blocks)
2112 cell = ''.join(blocks)
2112 2113
2113 2114 # Store raw and processed history
2114 self.history_manager.store_inputs(ipy_cell, cell)
2115 if store_history:
2116 self.history_manager.store_inputs(self.execution_count,
2117 cell, raw_cell)
2115 2118
2116 self.logger.log(ipy_cell, cell)
2119 self.logger.log(cell, raw_cell)
2117 2120
2118 2121 # All user code execution must happen with our context managers active
2119 2122 with nested(self.builtin_trap, self.display_trap):
2120 2123
2121 2124 # Single-block input should behave like an interactive prompt
2122 2125 if len(blocks) == 1:
2123 # since we return here, we need to update the execution count
2124 out = self.run_one_block(blocks[0])
2125 self.execution_count += 1
2126 out = self.run_source(blocks[0])
2127 # Write output to the database. Does nothing unless
2128 # history output logging is enabled.
2129 if store_history:
2130 self.history_manager.store_output(self.execution_count)
2131 # since we return here, we need to update the execution count
2132 self.execution_count += 1
2126 2133 return out
2127 2134
2128 2135 # In multi-block input, if the last block is a simple (one-two
2129 2136 # lines) expression, run it in single mode so it produces output.
2130 # Otherwise just feed the whole thing to run_code. This seems like
2131 # a reasonable usability design.
2137 # Otherwise just run it all in 'exec' mode. This seems like a
2138 # reasonable usability design.
2132 2139 last = blocks[-1]
2133 2140 last_nlines = len(last.splitlines())
2134
2135 # Note: below, whenever we call run_code, we must sync history
2136 # ourselves, because run_code is NOT meant to manage history at all.
2141
2137 2142 if last_nlines < 2:
2138 2143 # Here we consider the cell split between 'body' and 'last',
2139 2144 # store all history and execute 'body', and if successful, then
@@ -2144,55 +2149,19 b' class InteractiveShell(Configurable, Magic):'
2144 2149 retcode = self.run_source(ipy_body, symbol='exec',
2145 2150 post_execute=False)
2146 2151 if retcode==0:
2147 # And the last expression via runlines so it produces output
2148 self.run_one_block(last)
2152 # Last expression compiled as 'single' so it produces output
2153 self.run_source(last)
2149 2154 else:
2150 2155 # Run the whole cell as one entity, storing both raw and
2151 2156 # processed input in history
2152 2157 self.run_source(ipy_cell, symbol='exec')
2153 2158
2154 # Each cell is a *single* input, regardless of how many lines it has
2155 self.execution_count += 1
2156
2157 def run_one_block(self, block):
2158 """Run a single interactive block of source code.
2159
2160 If the block is single-line, dynamic transformations are applied to it
2161 (like automagics, autocall and alias recognition).
2162
2163 If the block is multi-line, it must consist of valid Python code only.
2164
2165 Parameters
2166 ----------
2167 block : string
2168 A (possibly multiline) string of code to be executed.
2169
2170 Returns
2171 -------
2172 The output of the underlying execution method used, be it
2173 :meth:`run_source` or :meth:`run_single_line`.
2174 """
2175 if len(block.splitlines()) <= 1:
2176 out = self.run_single_line(block)
2177 else:
2178 # Call run_source, which correctly compiles the input cell.
2179 # run_code must only be called when we know we have a code object,
2180 # as it does a naked exec and the compilation mode may not be what
2181 # we wanted.
2182 out = self.run_source(block)
2183 return out
2184
2185 def run_single_line(self, line):
2186 """Run a single-line interactive statement.
2187
2188 This assumes the input has been transformed to IPython syntax by
2189 applying all static transformations (those with an explicit prefix like
2190 % or !), but it will further try to apply the dynamic ones.
2191
2192 It does not update history.
2193 """
2194 tline = self.prefilter_manager.prefilter_line(line)
2195 return self.run_source(tline)
2159 # Write output to the database. Does nothing unless
2160 # history output logging is enabled.
2161 if store_history:
2162 self.history_manager.store_output(self.execution_count)
2163 # Each cell is a *single* input, regardless of how many lines it has
2164 self.execution_count += 1
2196 2165
2197 2166 # PENDING REMOVAL: this method is slated for deletion, once our new
2198 2167 # input logic has been 100% moved to frontends and is stable.
@@ -2205,8 +2174,8 b' class InteractiveShell(Configurable, Magic):'
2205 2174 magic calls (%magic), special shell access (!cmd), etc.
2206 2175 """
2207 2176
2208 if isinstance(lines, (list, tuple)):
2209 lines = '\n'.join(lines)
2177 if not isinstance(lines, (list, tuple)):
2178 lines = lines.splitlines()
2210 2179
2211 2180 if clean:
2212 2181 lines = self._cleanup_ipy_script(lines)
@@ -2214,7 +2183,6 b' class InteractiveShell(Configurable, Magic):'
2214 2183 # We must start with a clean buffer, in case this is run from an
2215 2184 # interactive IPython session (via a magic, for example).
2216 2185 self.reset_buffer()
2217 lines = lines.splitlines()
2218 2186
2219 2187 # Since we will prefilter all lines, store the user's raw input too
2220 2188 # before we apply any transformations
@@ -2401,8 +2369,8 b' class InteractiveShell(Configurable, Magic):'
2401 2369 full_source = '\n'.join(self.buffer)
2402 2370 more = self.run_source(full_source, self.filename)
2403 2371 if not more:
2404 self.history_manager.store_inputs('\n'.join(self.buffer_raw),
2405 full_source)
2372 self.history_manager.store_inputs(self.execution_count,
2373 '\n'.join(self.buffer_raw), full_source)
2406 2374 self.reset_buffer()
2407 2375 self.execution_count += 1
2408 2376 return more
@@ -2539,11 +2507,12 b' class InteractiveShell(Configurable, Magic):'
2539 2507 os.unlink(tfile)
2540 2508 except OSError:
2541 2509 pass
2542
2543 self.save_history()
2544
2510
2511 # Close the history session (this stores the end time and line count)
2512 self.history_manager.end_session()
2513
2545 2514 # Clear all user namespaces to release all references cleanly.
2546 self.reset()
2515 self.reset(new_session=False)
2547 2516
2548 2517 # Run user hooks
2549 2518 self.hooks.shutdown_hook()
@@ -8,9 +8,8 b''
8 8 #*****************************************************************************
9 9
10 10 import IPython.utils.io
11 from IPython.core.autocall import IPyAutocall
12 11
13 class Macro(IPyAutocall):
12 class Macro(object):
14 13 """Simple class to store the value of macros as strings.
15 14
16 15 Macro is just a callable that executes a string of IPython
@@ -19,9 +18,9 b' class Macro(IPyAutocall):'
19 18 Args to macro are available in _margv list if you need them.
20 19 """
21 20
22 def __init__(self,data):
21 def __init__(self,code):
23 22 """store the macro value, as a single string which can be executed"""
24 self.value = ''.join(data).rstrip()+'\n'
23 self.value = code.rstrip()+'\n'
25 24
26 25 def __str__(self):
27 26 return self.value
@@ -29,11 +28,6 b' class Macro(IPyAutocall):'
29 28 def __repr__(self):
30 29 return 'IPython.macro.Macro(%s)' % repr(self.value)
31 30
32 def __call__(self,*args):
33 IPython.utils.io.Term.cout.flush()
34 self._ip.user_ns['_margv'] = args
35 self._ip.run_cell(self.value)
36
37 31 def __getstate__(self):
38 32 """ needed for safe pickling via %store """
39 33 return {'value': self.value}
@@ -57,7 +57,7 b' import IPython.utils.io'
57 57 from IPython.utils.path import get_py_filename
58 58 from IPython.utils.process import arg_split, abbrev_cwd
59 59 from IPython.utils.terminal import set_term_title
60 from IPython.utils.text import LSString, SList, StringTypes, format_screen
60 from IPython.utils.text import LSString, SList, format_screen
61 61 from IPython.utils.timing import clock, clock2
62 62 from IPython.utils.warn import warn, error
63 63 from IPython.utils.ipstruct import Struct
@@ -165,14 +165,15 b' python-profiler package from non-free.""")'
165 165 out.sort()
166 166 return out
167 167
168 def extract_input_slices(self,slices,raw=False):
168 def extract_input_lines(self, range_str, raw=False):
169 169 """Return as a string a set of input history slices.
170 170
171 171 Inputs:
172 172
173 - slices: the set of slices is given as a list of strings (like
174 ['1','4:8','9'], since this function is for use by magic functions
175 which get their arguments as strings.
173 - range_str: the set of slices is given as a string, like
174 "~5/6-~4/2 4:8 9", since this function is for use by magic functions
175 which get their arguments as strings. The number before the / is the
176 session number: ~n goes n back from the current session.
176 177
177 178 Optional inputs:
178 179
@@ -184,24 +185,9 b' python-profiler package from non-free.""")'
184 185 N:M -> standard python form, means including items N...(M-1).
185 186
186 187 N-M -> include items N..M (closed endpoint)."""
187
188 if raw:
189 hist = self.shell.history_manager.input_hist_raw
190 else:
191 hist = self.shell.history_manager.input_hist_parsed
192
193 cmds = []
194 for chunk in slices:
195 if ':' in chunk:
196 ini,fin = map(int,chunk.split(':'))
197 elif '-' in chunk:
198 ini,fin = map(int,chunk.split('-'))
199 fin += 1
200 else:
201 ini = int(chunk)
202 fin = ini+1
203 cmds.append(''.join(hist[ini:fin]))
204 return cmds
188 lines = self.shell.history_manager.\
189 get_range_by_str(range_str, raw=raw)
190 return "\n".join(x for _, _, x in lines)
205 191
206 192 def arg_err(self,func):
207 193 """Print docstring if incorrect arguments were passed"""
@@ -1617,7 +1603,7 b' Currently the magic system has the following functions:\\n"""'
1617 1603
1618 1604 stats = None
1619 1605 try:
1620 self.shell.save_history()
1606 #self.shell.save_history()
1621 1607
1622 1608 if opts.has_key('p'):
1623 1609 stats = self.magic_prun('',0,opts,arg_lst,prog_ns)
@@ -1736,7 +1722,7 b' Currently the magic system has the following functions:\\n"""'
1736 1722 # contained therein.
1737 1723 del sys.modules[main_mod_name]
1738 1724
1739 self.shell.reload_history()
1725 #self.shell.reload_history()
1740 1726
1741 1727 return stats
1742 1728
@@ -1990,9 +1976,7 b' Currently the magic system has the following functions:\\n"""'
1990 1976 you had typed them. You just type 'name' at the prompt and the code
1991 1977 executes.
1992 1978
1993 The notation for indicating number ranges is: n1-n2 means 'use line
1994 numbers n1,...n2' (the endpoint is included). That is, '5-7' means
1995 using the lines numbered 5,6 and 7.
1979 The syntax for indicating input ranges is described in %history.
1996 1980
1997 1981 Note: as a 'hidden' feature, you can also use traditional python slice
1998 1982 notation, where N:M means numbers N through M-1.
@@ -2033,17 +2017,16 b' Currently the magic system has the following functions:\\n"""'
2033 2017 In [60]: exec In[44:48]+In[49]"""
2034 2018
2035 2019 opts,args = self.parse_options(parameter_s,'r',mode='list')
2036 if not args:
2037 macs = [k for k,v in self.shell.user_ns.items() if isinstance(v, Macro)]
2038 macs.sort()
2039 return macs
2020 if not args: # List existing macros
2021 return sorted(k for k,v in self.shell.user_ns.iteritems() if\
2022 isinstance(v, Macro))
2040 2023 if len(args) == 1:
2041 2024 raise UsageError(
2042 2025 "%macro insufficient args; usage '%macro name n1-n2 n3-4...")
2043 name,ranges = args[0], args[1:]
2026 name, ranges = args[0], " ".join(args[1:])
2044 2027
2045 2028 #print 'rng',ranges # dbg
2046 lines = self.extract_input_slices(ranges,opts.has_key('r'))
2029 lines = self.extract_input_lines(ranges,'r' in opts)
2047 2030 macro = Macro(lines)
2048 2031 self.shell.define_macro(name, macro)
2049 2032 print 'Macro `%s` created. To execute, type its name (without quotes).' % name
@@ -2063,15 +2046,14 b' Currently the magic system has the following functions:\\n"""'
2063 2046 Python. If this option is given, the raw input as typed as the
2064 2047 command line is used instead.
2065 2048
2066 This function uses the same syntax as %macro for line extraction, but
2067 instead of creating a macro it saves the resulting string to the
2068 filename you specify.
2049 This function uses the same syntax as %history for input ranges,
2050 then saves the lines to the filename you specify.
2069 2051
2070 2052 It adds a '.py' extension to the file if you don't do so yourself, and
2071 2053 it asks for confirmation before overwriting existing files."""
2072 2054
2073 2055 opts,args = self.parse_options(parameter_s,'r',mode='list')
2074 fname,ranges = args[0], args[1:]
2056 fname,ranges = args[0], " ".join(args[1:])
2075 2057 if not fname.endswith('.py'):
2076 2058 fname += '.py'
2077 2059 if os.path.isfile(fname):
@@ -2079,10 +2061,9 b' Currently the magic system has the following functions:\\n"""'
2079 2061 if ans.lower() not in ['y','yes']:
2080 2062 print 'Operation cancelled.'
2081 2063 return
2082 cmds = ''.join(self.extract_input_slices(ranges,opts.has_key('r')))
2083 f = file(fname,'w')
2084 f.write(cmds)
2085 f.close()
2064 cmds = self.extract_input_lines(ranges, 'r' in opts)
2065 with open(fname,'w') as f:
2066 f.write(cmds)
2086 2067 print 'The following commands were written to file `%s`:' % fname
2087 2068 print cmds
2088 2069
@@ -2154,15 +2135,17 b' Currently the magic system has the following functions:\\n"""'
2154 2135 Arguments:
2155 2136
2156 2137 If arguments are given, the following possibilites exist:
2138
2139 - If the argument is a filename, IPython will load that into the
2140 editor. It will execute its contents with execfile() when you exit,
2141 loading any code in the file into your interactive namespace.
2157 2142
2158 - The arguments are numbers or pairs of colon-separated numbers (like
2159 1 4:8 9). These are interpreted as lines of previous input to be
2160 loaded into the editor. The syntax is the same of the %macro command.
2143 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
2144 The syntax is the same as in the %history magic.
2161 2145
2162 - If the argument doesn't start with a number, it is evaluated as a
2163 variable and its contents loaded into the editor. You can thus edit
2164 any string which contains python code (including the result of
2165 previous edits).
2146 - If the argument is a string variable, its contents are loaded
2147 into the editor. You can thus edit any string which contains
2148 python code (including the result of previous edits).
2166 2149
2167 2150 - If the argument is the name of an object (other than a string),
2168 2151 IPython will try to locate the file where it was defined and open the
@@ -2179,11 +2162,6 b' Currently the magic system has the following functions:\\n"""'
2179 2162 '+NUMBER' parameter necessary for this feature. Good editors like
2180 2163 (X)Emacs, vi, jed, pico and joe all do.
2181 2164
2182 - If the argument is not found as a variable, IPython will look for a
2183 file with that name (adding .py if necessary) and load it into the
2184 editor. It will execute its contents with execfile() when you exit,
2185 loading any code in the file into your interactive namespace.
2186
2187 2165 After executing your code, %edit will return as output the code you
2188 2166 typed in the editor (except when it was an existing file). This way
2189 2167 you can reload the code in further invocations of %edit as a variable,
@@ -2266,13 +2244,13 b' Currently the magic system has the following functions:\\n"""'
2266 2244
2267 2245 opts,args = self.parse_options(parameter_s,'prxn:')
2268 2246 # Set a few locals from the options for convenience:
2269 opts_p = opts.has_key('p')
2270 opts_r = opts.has_key('r')
2247 opts_prev = 'p' in opts
2248 opts_raw = 'r' in opts
2271 2249
2272 2250 # Default line number value
2273 2251 lineno = opts.get('n',None)
2274 2252
2275 if opts_p:
2253 if opts_prev:
2276 2254 args = '_%s' % last_call[0]
2277 2255 if not self.shell.user_ns.has_key(args):
2278 2256 args = last_call[1]
@@ -2281,90 +2259,83 b' Currently the magic system has the following functions:\\n"""'
2281 2259 # let it be clobbered by successive '-p' calls.
2282 2260 try:
2283 2261 last_call[0] = self.shell.displayhook.prompt_count
2284 if not opts_p:
2262 if not opts_prev:
2285 2263 last_call[1] = parameter_s
2286 2264 except:
2287 2265 pass
2288 2266
2289 2267 # by default this is done with temp files, except when the given
2290 2268 # arg is a filename
2291 use_temp = 1
2269 use_temp = True
2292 2270
2293 if re.match(r'\d',args):
2294 # Mode where user specifies ranges of lines, like in %macro.
2295 # This means that you can't edit files whose names begin with
2296 # numbers this way. Tough.
2297 ranges = args.split()
2298 data = ''.join(self.extract_input_slices(ranges,opts_r))
2299 elif args.endswith('.py'):
2271 data = ''
2272 if args.endswith('.py'):
2300 2273 filename = make_filename(args)
2301 data = ''
2302 use_temp = 0
2274 use_temp = False
2303 2275 elif args:
2304 try:
2305 # Load the parameter given as a variable. If not a string,
2306 # process it as an object instead (below)
2307
2308 #print '*** args',args,'type',type(args) # dbg
2309 data = eval(args,self.shell.user_ns)
2310 if not type(data) in StringTypes:
2311 raise DataIsObject
2312
2313 except (NameError,SyntaxError):
2314 # given argument is not a variable, try as a filename
2315 filename = make_filename(args)
2316 if filename is None:
2317 warn("Argument given (%s) can't be found as a variable "
2318 "or as a filename." % args)
2319 return
2276 # Mode where user specifies ranges of lines, like in %macro.
2277 data = self.extract_input_lines(args, opts_raw)
2278 if not data:
2279 try:
2280 # Load the parameter given as a variable. If not a string,
2281 # process it as an object instead (below)
2320 2282
2321 data = ''
2322 use_temp = 0
2323 except DataIsObject:
2283 #print '*** args',args,'type',type(args) # dbg
2284 data = eval(args, self.shell.user_ns)
2285 if not isinstance(data, basestring):
2286 raise DataIsObject
2324 2287
2325 # macros have a special edit function
2326 if isinstance(data,Macro):
2327 self._edit_macro(args,data)
2328 return
2329
2330 # For objects, try to edit the file where they are defined
2331 try:
2332 filename = inspect.getabsfile(data)
2333 if 'fakemodule' in filename.lower() and inspect.isclass(data):
2334 # class created by %edit? Try to find source
2335 # by looking for method definitions instead, the
2336 # __module__ in those classes is FakeModule.
2337 attrs = [getattr(data, aname) for aname in dir(data)]
2338 for attr in attrs:
2339 if not inspect.ismethod(attr):
2340 continue
2341 filename = inspect.getabsfile(attr)
2342 if filename and 'fakemodule' not in filename.lower():
2343 # change the attribute to be the edit target instead
2344 data = attr
2345 break
2346
2347 datafile = 1
2348 except TypeError:
2288 except (NameError,SyntaxError):
2289 # given argument is not a variable, try as a filename
2349 2290 filename = make_filename(args)
2350 datafile = 1
2351 warn('Could not find file where `%s` is defined.\n'
2352 'Opening a file named `%s`' % (args,filename))
2353 # Now, make sure we can actually read the source (if it was in
2354 # a temp file it's gone by now).
2355 if datafile:
2291 if filename is None:
2292 warn("Argument given (%s) can't be found as a variable "
2293 "or as a filename." % args)
2294 return
2295 use_temp = False
2296
2297 except DataIsObject:
2298 # macros have a special edit function
2299 if isinstance(data, Macro):
2300 self._edit_macro(args,data)
2301 return
2302
2303 # For objects, try to edit the file where they are defined
2356 2304 try:
2357 if lineno is None:
2358 lineno = inspect.getsourcelines(data)[1]
2359 except IOError:
2305 filename = inspect.getabsfile(data)
2306 if 'fakemodule' in filename.lower() and inspect.isclass(data):
2307 # class created by %edit? Try to find source
2308 # by looking for method definitions instead, the
2309 # __module__ in those classes is FakeModule.
2310 attrs = [getattr(data, aname) for aname in dir(data)]
2311 for attr in attrs:
2312 if not inspect.ismethod(attr):
2313 continue
2314 filename = inspect.getabsfile(attr)
2315 if filename and 'fakemodule' not in filename.lower():
2316 # change the attribute to be the edit target instead
2317 data = attr
2318 break
2319
2320 datafile = 1
2321 except TypeError:
2360 2322 filename = make_filename(args)
2361 if filename is None:
2362 warn('The file `%s` where `%s` was defined cannot '
2363 'be read.' % (filename,data))
2364 return
2365 use_temp = 0
2366 else:
2367 data = ''
2323 datafile = 1
2324 warn('Could not find file where `%s` is defined.\n'
2325 'Opening a file named `%s`' % (args,filename))
2326 # Now, make sure we can actually read the source (if it was in
2327 # a temp file it's gone by now).
2328 if datafile:
2329 try:
2330 if lineno is None:
2331 lineno = inspect.getsourcelines(data)[1]
2332 except IOError:
2333 filename = make_filename(args)
2334 if filename is None:
2335 warn('The file `%s` where `%s` was defined cannot '
2336 'be read.' % (filename,data))
2337 return
2338 use_temp = False
2368 2339
2369 2340 if use_temp:
2370 2341 filename = self.shell.mktempfile(data)
@@ -2387,12 +2358,13 b' Currently the magic system has the following functions:\\n"""'
2387 2358 if args.strip() == 'pasted_block':
2388 2359 self.shell.user_ns['pasted_block'] = file_read(filename)
2389 2360
2390 if opts.has_key('x'): # -x prevents actual execution
2361 if 'x' in opts: # -x prevents actual execution
2391 2362 print
2392 2363 else:
2393 2364 print 'done. Executing edited code...'
2394 if opts_r:
2395 self.shell.run_cell(file_read(filename))
2365 if opts_raw:
2366 self.shell.run_cell(file_read(filename),
2367 store_history=False)
2396 2368 else:
2397 2369 self.shell.safe_execfile(filename,self.shell.user_ns,
2398 2370 self.shell.user_ns)
@@ -3050,38 +3022,6 b' Defaulting color scheme to \'NoColor\'"""'
3050 3022 if parameter_s:
3051 3023 return self.shell.getoutput(parameter_s)
3052 3024
3053 def magic_r(self, parameter_s=''):
3054 """Repeat previous input.
3055
3056 Note: Consider using the more powerfull %rep instead!
3057
3058 If given an argument, repeats the previous command which starts with
3059 the same string, otherwise it just repeats the previous input.
3060
3061 Shell escaped commands (with ! as first character) are not recognized
3062 by this system, only pure python code and magic commands.
3063 """
3064
3065 start = parameter_s.strip()
3066 esc_magic = ESC_MAGIC
3067 # Identify magic commands even if automagic is on (which means
3068 # the in-memory version is different from that typed by the user).
3069 if self.shell.automagic:
3070 start_magic = esc_magic+start
3071 else:
3072 start_magic = start
3073 # Look through the input history in reverse
3074 for n in range(len(self.shell.history_manager.input_hist_parsed)-2,0,-1):
3075 input = self.shell.history_manager.input_hist_parsed[n]
3076 # skip plain 'r' lines so we don't recurse to infinity
3077 if input != '_ip.magic("r")\n' and \
3078 (input.startswith(start) or input.startswith(start_magic)):
3079 #print 'match',`input` # dbg
3080 print 'Executing:',input,
3081 self.shell.run_cell(input)
3082 return
3083 print 'No previous input matching `%s` found.' % start
3084
3085 3025
3086 3026 def magic_bookmark(self, parameter_s=''):
3087 3027 """Manage IPython's bookmark system.
@@ -32,6 +32,7 b' import re'
32 32 from IPython.core.alias import AliasManager
33 33 from IPython.core.autocall import IPyAutocall
34 34 from IPython.config.configurable import Configurable
35 from IPython.core.macro import Macro
35 36 from IPython.core.splitinput import split_user_input
36 37 from IPython.core import page
37 38
@@ -598,6 +599,18 b' class ShellEscapeChecker(PrefilterChecker):'
598 599 return self.prefilter_manager.get_handler_by_name('shell')
599 600
600 601
602 class MacroChecker(PrefilterChecker):
603
604 priority = Int(250, config=True)
605
606 def check(self, line_info):
607 obj = self.shell.user_ns.get(line_info.ifun)
608 if isinstance(obj, Macro):
609 return self.prefilter_manager.get_handler_by_name('macro')
610 else:
611 return None
612
613
601 614 class IPyAutocallChecker(PrefilterChecker):
602 615
603 616 priority = Int(300, config=True)
@@ -837,6 +850,16 b' class ShellEscapeHandler(PrefilterHandler):'
837 850 return line_out
838 851
839 852
853 class MacroHandler(PrefilterHandler):
854 handler_name = Str("macro")
855
856 def handle(self, line_info):
857 obj = self.shell.user_ns.get(line_info.ifun)
858 pre_space = line_info.pre_whitespace
859 line_sep = "\n" + pre_space
860 return pre_space + line_sep.join(obj.value.splitlines())
861
862
840 863 class MagicHandler(PrefilterHandler):
841 864
842 865 handler_name = Str('magic')
@@ -979,6 +1002,7 b' _default_transformers = ['
979 1002 _default_checkers = [
980 1003 EmacsChecker,
981 1004 ShellEscapeChecker,
1005 MacroChecker,
982 1006 IPyAutocallChecker,
983 1007 MultiLineMagicChecker,
984 1008 EscCharsChecker,
@@ -993,6 +1017,7 b' _default_handlers = ['
993 1017 PrefilterHandler,
994 1018 AliasHandler,
995 1019 ShellEscapeHandler,
1020 MacroHandler,
996 1021 MagicHandler,
997 1022 AutoHandler,
998 1023 HelpHandler,
@@ -14,35 +14,101 b' import nose.tools as nt'
14 14
15 15 # our own packages
16 16 from IPython.utils.tempdir import TemporaryDirectory
17 from IPython.core.history import HistoryManager
17 from IPython.core.history import HistoryManager, extract_hist_ranges
18 18
19 19 def test_history():
20 20
21 21 ip = get_ipython()
22 22 with TemporaryDirectory() as tmpdir:
23 23 #tmpdir = '/software/temp'
24 histfile = os.path.realpath(os.path.join(tmpdir, 'history.json'))
24 histfile = os.path.realpath(os.path.join(tmpdir, 'history.sqlite'))
25 25 # Ensure that we restore the history management that we mess with in
26 26 # this test doesn't affect the IPython instance used by the test suite
27 27 # beyond this test.
28 28 hist_manager_ori = ip.history_manager
29 29 try:
30 ip.history_manager = HistoryManager(ip)
30 ip.history_manager = HistoryManager(shell=ip)
31 31 ip.history_manager.hist_file = histfile
32 ip.history_manager.init_db() # Has to be called after changing file
33 ip.history_manager.reset()
32 34 print 'test',histfile
33 hist = ['a=1\n', 'def f():\n test = 1\n return test\n', 'b=2\n']
34 # test save and load
35 ip.history_manager.input_hist_raw[:] = []
36 for h in hist:
37 ip.history_manager.input_hist_raw.append(h)
38 ip.save_history()
39 ip.history_manager.input_hist_raw[:] = []
40 ip.reload_history()
41 print type(ip.history_manager.input_hist_raw)
42 print ip.history_manager.input_hist_raw
43 nt.assert_equal(len(ip.history_manager.input_hist_raw), len(hist))
44 for i,h in enumerate(hist):
45 nt.assert_equal(hist[i], ip.history_manager.input_hist_raw[i])
35 hist = ['a=1', 'def f():\n test = 1\n return test', 'b=2']
36 for i, h in enumerate(hist, start=1):
37 ip.history_manager.store_inputs(i, h)
38
39 ip.history_manager.db_log_output = True
40 # Doesn't match the input, but we'll just check it's stored.
41 ip.history_manager.output_hist_reprs[3].append("spam")
42 ip.history_manager.store_output(3)
43
44 nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist)
45
46 # Check lines were written to DB
47 c = ip.history_manager.db.execute("SELECT source_raw FROM history")
48 nt.assert_equal([x for x, in c], hist)
49
50 # New session
51 ip.history_manager.reset()
52 newcmds = ["z=5","class X(object):\n pass", "k='p'"]
53 for i, cmd in enumerate(newcmds, start=1):
54 ip.history_manager.store_inputs(i, cmd)
55 gothist = ip.history_manager.get_range(start=1, stop=4)
56 nt.assert_equal(list(gothist), zip([0,0,0],[1,2,3], newcmds))
57 # Previous session:
58 gothist = ip.history_manager.get_range(-1, 1, 4)
59 nt.assert_equal(list(gothist), zip([1,1,1],[1,2,3], hist))
60
61 # Check get_hist_tail
62 gothist = ip.history_manager.get_tail(4, output=True,
63 include_latest=True)
64 expected = [(1, 3, (hist[-1], ["spam"])),
65 (2, 1, (newcmds[0], None)),
66 (2, 2, (newcmds[1], None)),
67 (2, 3, (newcmds[2], None)),]
68 nt.assert_equal(list(gothist), expected)
69
70 gothist = ip.history_manager.get_tail(2)
71 expected = [(2, 1, newcmds[0]),
72 (2, 2, newcmds[1])]
73 nt.assert_equal(list(gothist), expected)
74
75 # Check get_hist_search
76 gothist = ip.history_manager.search("*test*")
77 nt.assert_equal(list(gothist), [(1,2,hist[1])] )
78 gothist = ip.history_manager.search("b*", output=True)
79 nt.assert_equal(list(gothist), [(1,3,(hist[2],["spam"]))] )
80
81 # Cross testing: check that magic %save can get previous session.
82 testfilename = os.path.realpath(os.path.join(tmpdir, "test.py"))
83 ip.magic_save(testfilename + " ~1/1-3")
84 testfile = open(testfilename, "r")
85 nt.assert_equal(testfile.read(), "\n".join(hist))
86
87 # Duplicate line numbers - check that it doesn't crash, and
88 # gets a new session
89 ip.history_manager.store_inputs(1, "rogue")
90 nt.assert_equal(ip.history_manager.session_number, 3)
46 91 finally:
47 92 # Restore history manager
48 93 ip.history_manager = hist_manager_ori
94
95 def test_extract_hist_ranges():
96 instr = "1 2/3 ~4/5-6 ~4/7-~4/9 ~9/2-~7/5"
97 expected = [(0, 1, 2), # 0 == current session
98 (2, 3, 4),
99 (-4, 5, 7),
100 (-4, 7, 10),
101 (-9, 2, None), # None == to end
102 (-8, 1, None),
103 (-7, 1, 6)]
104 actual = list(extract_hist_ranges(instr))
105 nt.assert_equal(actual, expected)
106
107 def test_magic_rerun():
108 """Simple test for %rerun (no args -> rerun last line)"""
109 ip = get_ipython()
110 ip.run_cell("a = 10")
111 ip.run_cell("a += 1")
112 nt.assert_equal(ip.user_ns["a"], 11)
113 ip.run_cell("%rerun")
114 nt.assert_equal(ip.user_ns["a"], 12)
@@ -62,7 +62,7 b' def doctest_hist_f():'
62 62
63 63 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
64 64
65 In [11]: %hist -n -f $tfile 3
65 In [11]: %hist -nl -f $tfile 3
66 66
67 67 In [13]: import os; os.unlink(tfile)
68 68 """
@@ -80,7 +80,7 b' def doctest_hist_r():'
80 80
81 81 In [2]: x=1
82 82
83 In [3]: %hist -r 2
83 In [3]: %hist -rl 2
84 84 x=1 # random
85 85 %hist -r 2
86 86 """
@@ -150,29 +150,38 b' def doctest_hist_op():'
150 150 <...s instance at ...>
151 151 >>>
152 152 """
153
154 def test_shist():
155 # Simple tests of ShadowHist class - test generator.
156 import os, shutil, tempfile
157
158 from IPython.utils import pickleshare
159 from IPython.core.history import ShadowHist
160
161 tfile = tempfile.mktemp('','tmp-ipython-')
162
163 db = pickleshare.PickleShareDB(tfile)
164 s = ShadowHist(db, get_ipython())
165 s.add('hello')
166 s.add('world')
167 s.add('hello')
168 s.add('hello')
169 s.add('karhu')
170
171 yield nt.assert_equals,s.all(),[(1, 'hello'), (2, 'world'), (3, 'karhu')]
172
173 yield nt.assert_equal,s.get(2),'world'
153
154 def test_macro():
155 ip = get_ipython()
156 ip.history_manager.reset() # Clear any existing history.
157 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
158 for i, cmd in enumerate(cmds, start=1):
159 ip.history_manager.store_inputs(i, cmd)
160 ip.magic("macro test 1-3")
161 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
174 162
175 shutil.rmtree(tfile)
163 # List macros.
164 assert "test" in ip.magic("macro")
165
166 def test_macro_run():
167 """Test that we can run a multi-line macro successfully."""
168 ip = get_ipython()
169 ip.history_manager.reset()
170 cmds = ["a=10", "a+=1", "print a", "%macro test 2-3"]
171 for cmd in cmds:
172 ip.run_cell(cmd)
173 nt.assert_equal(ip.user_ns["test"].value, "a+=1\nprint a\n")
174 original_stdout = sys.stdout
175 new_stdout = StringIO()
176 sys.stdout = new_stdout
177 try:
178 ip.run_cell("test")
179 nt.assert_true("12" in new_stdout.getvalue())
180 ip.run_cell("test")
181 nt.assert_true("13" in new_stdout.getvalue())
182 finally:
183 sys.stdout = original_stdout
184 new_stdout.close()
176 185
177 186
178 187 # XXX failing for now, until we get clearcmd out of quarantine. But we should
@@ -158,15 +158,12 b' class IPythonWidget(FrontendWidget):'
158 158 else:
159 159 super(IPythonWidget, self)._handle_execute_reply(msg)
160 160
161 def _handle_history_reply(self, msg):
162 """ Implemented to handle history replies, which are only supported by
163 the IPython kernel.
161 def _handle_history_tail_reply(self, msg):
162 """ Implemented to handle history tail replies, which are only supported
163 by the IPython kernel.
164 164 """
165 history_dict = msg['content']['history']
166 input_history_dict = {}
167 for key,val in history_dict.items():
168 input_history_dict[int(key)] = val
169 items = [ val.rstrip() for _, val in sorted(input_history_dict.items()) ]
165 history_items = msg['content']['history']
166 items = [ line.rstrip() for _, _, line in history_items ]
170 167 self._set_history(items)
171 168
172 169 def _handle_pyout(self, msg):
@@ -213,7 +210,7 b' class IPythonWidget(FrontendWidget):'
213 210 """ Reimplemented to make a history request.
214 211 """
215 212 super(IPythonWidget, self)._started_channels()
216 self.kernel_manager.xreq_channel.history(raw=True, output=False)
213 self.kernel_manager.xreq_channel.history_tail(1000)
217 214
218 215 #---------------------------------------------------------------------------
219 216 # 'ConsoleWidget' public interface
@@ -185,10 +185,6 b' class TerminalInteractiveShell(InteractiveShell):'
185 185
186 186 with nested(self.builtin_trap, self.display_trap):
187 187
188 # if you run stuff with -c <cmd>, raw hist is not updated
189 # ensure that it's in sync
190 self.history_manager.sync_inputs()
191
192 188 while 1:
193 189 try:
194 190 self.interact(display_banner=display_banner)
@@ -286,67 +282,6 b' class TerminalInteractiveShell(InteractiveShell):'
286 282 # Turn off the exit flag, so the mainloop can be restarted if desired
287 283 self.exit_now = False
288 284
289 def raw_input(self, prompt='', continue_prompt=False):
290 """Write a prompt and read a line.
291
292 The returned line does not include the trailing newline.
293 When the user enters the EOF key sequence, EOFError is raised.
294
295 Optional inputs:
296
297 - prompt(''): a string to be printed to prompt the user.
298
299 - continue_prompt(False): whether this line is the first one or a
300 continuation in a sequence of inputs.
301 """
302 # Code run by the user may have modified the readline completer state.
303 # We must ensure that our completer is back in place.
304
305 if self.has_readline:
306 self.set_readline_completer()
307
308 try:
309 line = raw_input_original(prompt).decode(self.stdin_encoding)
310 except ValueError:
311 warn("\n********\nYou or a %run:ed script called sys.stdin.close()"
312 " or sys.stdout.close()!\nExiting IPython!")
313 self.ask_exit()
314 return ""
315
316 # Try to be reasonably smart about not re-indenting pasted input more
317 # than necessary. We do this by trimming out the auto-indent initial
318 # spaces, if the user's actual input started itself with whitespace.
319 if self.autoindent:
320 if num_ini_spaces(line) > self.indent_current_nsp:
321 line = line[self.indent_current_nsp:]
322 self.indent_current_nsp = 0
323
324 # store the unfiltered input before the user has any chance to modify
325 # it.
326 if line.strip():
327 if continue_prompt:
328 if self.has_readline and self.readline_use:
329 histlen = self.readline.get_current_history_length()
330 if histlen > 1:
331 newhist = self.history_manager.input_hist_raw[-1].rstrip()
332 self.readline.remove_history_item(histlen-1)
333 self.readline.replace_history_item(histlen-2,
334 newhist.encode(self.stdin_encoding))
335 else:
336 self.history_manager.input_hist_raw.append('%s\n' % line)
337 elif not continue_prompt:
338 self.history_manager.input_hist_raw.append('\n')
339 try:
340 lineout = self.prefilter_manager.prefilter_lines(line,continue_prompt)
341 except:
342 # blanket except, in case a user-defined prefilter crashes, so it
343 # can't take all of ipython with it.
344 self.showtraceback()
345 return ''
346 else:
347 return lineout
348
349
350 285 def raw_input(self, prompt=''):
351 286 """Write a prompt and read a line.
352 287
@@ -19,7 +19,6 b' import __main__'
19 19 import os
20 20 import re
21 21 import shutil
22 import types
23 22
24 23 from IPython.external.path import path
25 24
@@ -30,8 +29,6 b' from IPython.utils.data import flatten'
30 29 # Code
31 30 #-----------------------------------------------------------------------------
32 31
33 StringTypes = types.StringTypes
34
35 32
36 33 def unquote_ends(istr):
37 34 """Remove a single pair of quotes from the endpoints of a string."""
@@ -325,7 +322,7 b' def qw(words,flat=0,sep=None,maxsplit=-1):'
325 322 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
326 323 """
327 324
328 if type(words) in StringTypes:
325 if isinstance(words, basestring):
329 326 return [word.strip() for word in words.split(sep,maxsplit)
330 327 if word and not word.isspace() ]
331 328 if flat:
@@ -345,7 +342,7 b' def qw_lol(indata):'
345 342 We need this to make sure the modules_some keys *always* end up as a
346 343 list of lists."""
347 344
348 if type(indata) in StringTypes:
345 if isinstance(indata, basestring):
349 346 return [qw(indata)]
350 347 else:
351 348 return qw(indata)
@@ -122,7 +122,7 b' class Kernel(Configurable):'
122 122
123 123 # Build dict of handlers for message types
124 124 msg_types = [ 'execute_request', 'complete_request',
125 'object_info_request', 'history_request',
125 'object_info_request', 'history_tail_request',
126 126 'connect_request', 'shutdown_request']
127 127 self.handlers = {}
128 128 for msg_type in msg_types:
@@ -323,13 +323,15 b' class Kernel(Configurable):'
323 323 oinfo, parent, ident)
324 324 logger.debug(msg)
325 325
326 def history_request(self, ident, parent):
327 output = parent['content']['output']
328 index = parent['content']['index']
326 def history_tail_request(self, ident, parent):
327 # We need to pull these out, as passing **kwargs doesn't work with
328 # unicode keys before Python 2.6.5.
329 n = parent['content']['n']
329 330 raw = parent['content']['raw']
330 hist = self.shell.get_history(index=index, raw=raw, output=output)
331 content = {'history' : hist}
332 msg = self.session.send(self.reply_socket, 'history_reply',
331 output = parent['content']['output']
332 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output)
333 content = {'history' : list(hist)}
334 msg = self.session.send(self.reply_socket, 'history_tail_reply',
333 335 content, parent, ident)
334 336 logger.debug(str(msg))
335 337
@@ -282,15 +282,13 b' class XReqSocketChannel(ZmqSocketChannel):'
282 282 self._queue_request(msg)
283 283 return msg['header']['msg_id']
284 284
285 def history(self, index=None, raw=False, output=True):
285 def history_tail(self, n=10, raw=True, output=False):
286 286 """Get the history list.
287 287
288 288 Parameters
289 289 ----------
290 index : n or (n1, n2) or None
291 If n, then the last entries. If a tuple, then all in
292 range(n1, n2). If None, then all entries. Raises IndexError if
293 the format of index is incorrect.
290 n : int
291 The number of lines of history to get.
294 292 raw : bool
295 293 If True, return the raw input.
296 294 output : bool
@@ -300,8 +298,8 b' class XReqSocketChannel(ZmqSocketChannel):'
300 298 -------
301 299 The msg_id of the message sent.
302 300 """
303 content = dict(index=index, raw=raw, output=output)
304 msg = self.session.msg('history_request', content)
301 content = dict(n=n, raw=raw, output=output)
302 msg = self.session.msg('history_tail_request', content)
305 303 self._queue_request(msg)
306 304 return msg['header']['msg_id']
307 305
@@ -18,7 +18,6 b' from __future__ import print_function'
18 18 # Stdlib
19 19 import inspect
20 20 import os
21 import re
22 21
23 22 # Our own
24 23 from IPython.core.interactiveshell import (
@@ -31,7 +30,6 b' from IPython.core.macro import Macro'
31 30 from IPython.core.payloadpage import install_payload_page
32 31 from IPython.utils import io
33 32 from IPython.utils.path import get_py_filename
34 from IPython.utils.text import StringTypes
35 33 from IPython.utils.traitlets import Instance, Type, Dict
36 34 from IPython.utils.warn import warn
37 35 from IPython.zmq.session import extract_header
@@ -433,9 +431,10 b' class ZMQInteractiveShell(InteractiveShell):'
433 431
434 432 # by default this is done with temp files, except when the given
435 433 # arg is a filename
436 use_temp = 1
434 use_temp = True
437 435
438 if re.match(r'\d',args):
436 data = ''
437 if args[0].isdigit():
439 438 # Mode where user specifies ranges of lines, like in %macro.
440 439 # This means that you can't edit files whose names begin with
441 440 # numbers this way. Tough.
@@ -443,16 +442,15 b' class ZMQInteractiveShell(InteractiveShell):'
443 442 data = ''.join(self.extract_input_slices(ranges,opts_r))
444 443 elif args.endswith('.py'):
445 444 filename = make_filename(args)
446 data = ''
447 use_temp = 0
445 use_temp = False
448 446 elif args:
449 447 try:
450 448 # Load the parameter given as a variable. If not a string,
451 449 # process it as an object instead (below)
452 450
453 451 #print '*** args',args,'type',type(args) # dbg
454 data = eval(args,self.shell.user_ns)
455 if not type(data) in StringTypes:
452 data = eval(args, self.shell.user_ns)
453 if not isinstance(data, basestring):
456 454 raise DataIsObject
457 455
458 456 except (NameError,SyntaxError):
@@ -462,13 +460,11 b' class ZMQInteractiveShell(InteractiveShell):'
462 460 warn("Argument given (%s) can't be found as a variable "
463 461 "or as a filename." % args)
464 462 return
465
466 data = ''
467 use_temp = 0
463 use_temp = False
464
468 465 except DataIsObject:
469
470 466 # macros have a special edit function
471 if isinstance(data,Macro):
467 if isinstance(data, Macro):
472 468 self._edit_macro(args,data)
473 469 return
474 470
@@ -507,9 +503,7 b' class ZMQInteractiveShell(InteractiveShell):'
507 503 warn('The file `%s` where `%s` was defined cannot '
508 504 'be read.' % (filename,data))
509 505 return
510 use_temp = 0
511 else:
512 data = ''
506 use_temp = False
513 507
514 508 if use_temp:
515 509 filename = self.shell.mktempfile(data)
General Comments 0
You need to be logged in to leave comments. Login now