##// END OF EJS Templates
Separate 'Out' in user_ns from the output history logging.
Thomas Kluyver -
Show More
@@ -1,325 +1,327 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Displayhook for IPython.
3 3
4 4 This defines a callable class that IPython uses for `sys.displayhook`.
5 5
6 6 Authors:
7 7
8 8 * Fernando Perez
9 9 * Brian Granger
10 10 * Robert Kern
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Copyright (C) 2008-2010 The IPython Development Team
15 15 # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu>
16 16 #
17 17 # Distributed under the terms of the BSD License. The full license is in
18 18 # the file COPYING, distributed as part of this software.
19 19 #-----------------------------------------------------------------------------
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Imports
23 23 #-----------------------------------------------------------------------------
24 24
25 25 import __builtin__
26 26
27 27 from IPython.config.configurable import Configurable
28 28 from IPython.core import prompts
29 29 import IPython.utils.generics
30 30 import IPython.utils.io
31 31 from IPython.utils.traitlets import Instance, List
32 32 from IPython.utils.warn import warn
33 33
34 34 #-----------------------------------------------------------------------------
35 35 # Main displayhook class
36 36 #-----------------------------------------------------------------------------
37 37
38 38 # TODO: The DisplayHook class should be split into two classes, one that
39 39 # manages the prompts and their synchronization and another that just does the
40 40 # displayhook logic and calls into the prompt manager.
41 41
42 42 # TODO: Move the various attributes (cache_size, colors, input_sep,
43 43 # output_sep, output_sep2, ps1, ps2, ps_out, pad_left). Some of these are also
44 44 # attributes of InteractiveShell. They should be on ONE object only and the
45 45 # other objects should ask that one object for their values.
46 46
47 47 class DisplayHook(Configurable):
48 48 """The custom IPython displayhook to replace sys.displayhook.
49 49
50 50 This class does many things, but the basic idea is that it is a callable
51 51 that gets called anytime user code returns a value.
52 52
53 53 Currently this class does more than just the displayhook logic and that
54 54 extra logic should eventually be moved out of here.
55 55 """
56 56
57 57 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
58 58
59 59 def __init__(self, shell=None, cache_size=1000,
60 60 colors='NoColor', input_sep='\n',
61 61 output_sep='\n', output_sep2='',
62 62 ps1 = None, ps2 = None, ps_out = None, pad_left=True,
63 63 config=None):
64 64 super(DisplayHook, self).__init__(shell=shell, config=config)
65 65
66 66 cache_size_min = 3
67 67 if cache_size <= 0:
68 68 self.do_full_cache = 0
69 69 cache_size = 0
70 70 elif cache_size < cache_size_min:
71 71 self.do_full_cache = 0
72 72 cache_size = 0
73 73 warn('caching was disabled (min value for cache size is %s).' %
74 74 cache_size_min,level=3)
75 75 else:
76 76 self.do_full_cache = 1
77 77
78 78 self.cache_size = cache_size
79 79 self.input_sep = input_sep
80 80
81 81 # we need a reference to the user-level namespace
82 82 self.shell = shell
83 83
84 84 # Set input prompt strings and colors
85 85 if cache_size == 0:
86 86 if ps1.find('%n') > -1 or ps1.find(r'\#') > -1 \
87 87 or ps1.find(r'\N') > -1:
88 88 ps1 = '>>> '
89 89 if ps2.find('%n') > -1 or ps2.find(r'\#') > -1 \
90 90 or ps2.find(r'\N') > -1:
91 91 ps2 = '... '
92 92 self.ps1_str = self._set_prompt_str(ps1,'In [\\#]: ','>>> ')
93 93 self.ps2_str = self._set_prompt_str(ps2,' .\\D.: ','... ')
94 94 self.ps_out_str = self._set_prompt_str(ps_out,'Out[\\#]: ','')
95 95
96 96 self.color_table = prompts.PromptColors
97 97 self.prompt1 = prompts.Prompt1(self,sep=input_sep,prompt=self.ps1_str,
98 98 pad_left=pad_left)
99 99 self.prompt2 = prompts.Prompt2(self,prompt=self.ps2_str,pad_left=pad_left)
100 100 self.prompt_out = prompts.PromptOut(self,sep='',prompt=self.ps_out_str,
101 101 pad_left=pad_left)
102 102 self.set_colors(colors)
103 103
104 104 # Store the last prompt string each time, we need it for aligning
105 105 # continuation and auto-rewrite prompts
106 106 self.last_prompt = ''
107 107 self.output_sep = output_sep
108 108 self.output_sep2 = output_sep2
109 109 self._,self.__,self.___ = '','',''
110 110
111 111 # these are deliberately global:
112 112 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
113 113 self.shell.user_ns.update(to_user_ns)
114 114
115 115 @property
116 116 def prompt_count(self):
117 117 return self.shell.execution_count
118 118
119 119 def _set_prompt_str(self,p_str,cache_def,no_cache_def):
120 120 if p_str is None:
121 121 if self.do_full_cache:
122 122 return cache_def
123 123 else:
124 124 return no_cache_def
125 125 else:
126 126 return p_str
127 127
128 128 def set_colors(self, colors):
129 129 """Set the active color scheme and configure colors for the three
130 130 prompt subsystems."""
131 131
132 132 # FIXME: This modifying of the global prompts.prompt_specials needs
133 133 # to be fixed. We need to refactor all of the prompts stuff to use
134 134 # proper configuration and traits notifications.
135 135 if colors.lower()=='nocolor':
136 136 prompts.prompt_specials = prompts.prompt_specials_nocolor
137 137 else:
138 138 prompts.prompt_specials = prompts.prompt_specials_color
139 139
140 140 self.color_table.set_active_scheme(colors)
141 141 self.prompt1.set_colors()
142 142 self.prompt2.set_colors()
143 143 self.prompt_out.set_colors()
144 144
145 145 #-------------------------------------------------------------------------
146 146 # Methods used in __call__. Override these methods to modify the behavior
147 147 # of the displayhook.
148 148 #-------------------------------------------------------------------------
149 149
150 150 def check_for_underscore(self):
151 151 """Check if the user has set the '_' variable by hand."""
152 152 # If something injected a '_' variable in __builtin__, delete
153 153 # ipython's automatic one so we don't clobber that. gettext() in
154 154 # particular uses _, so we need to stay away from it.
155 155 if '_' in __builtin__.__dict__:
156 156 try:
157 157 del self.shell.user_ns['_']
158 158 except KeyError:
159 159 pass
160 160
161 161 def quiet(self):
162 162 """Should we silence the display hook because of ';'?"""
163 163 # do not print output if input ends in ';'
164 164 try:
165 165 if self.shell.history_manager.input_hist_parsed[self.prompt_count].endswith(';\n'):
166 166 return True
167 167 except IndexError:
168 168 # some uses of ipshellembed may fail here
169 169 pass
170 170 return False
171 171
172 172 def start_displayhook(self):
173 173 """Start the displayhook, initializing resources."""
174 174 pass
175 175
176 176 def write_output_prompt(self):
177 177 """Write the output prompt.
178 178
179 179 The default implementation simply writes the prompt to
180 180 ``io.Term.cout``.
181 181 """
182 182 # Use write, not print which adds an extra space.
183 183 IPython.utils.io.Term.cout.write(self.output_sep)
184 184 outprompt = str(self.prompt_out)
185 185 if self.do_full_cache:
186 186 IPython.utils.io.Term.cout.write(outprompt)
187 187
188 188 def compute_format_data(self, result):
189 189 """Compute format data of the object to be displayed.
190 190
191 191 The format data is a generalization of the :func:`repr` of an object.
192 192 In the default implementation the format data is a :class:`dict` of
193 193 key value pair where the keys are valid MIME types and the values
194 194 are JSON'able data structure containing the raw data for that MIME
195 195 type. It is up to frontends to determine pick a MIME to to use and
196 196 display that data in an appropriate manner.
197 197
198 198 This method only computes the format data for the object and should
199 199 NOT actually print or write that to a stream.
200 200
201 201 Parameters
202 202 ----------
203 203 result : object
204 204 The Python object passed to the display hook, whose format will be
205 205 computed.
206 206
207 207 Returns
208 208 -------
209 209 format_data : dict
210 210 A :class:`dict` whose keys are valid MIME types and values are
211 211 JSON'able raw data for that MIME type. It is recommended that
212 212 all return values of this should always include the "text/plain"
213 213 MIME type representation of the object.
214 214 """
215 215 return self.shell.display_formatter.format(result)
216 216
217 217 def write_format_data(self, format_dict):
218 218 """Write the format data dict to the frontend.
219 219
220 220 This default version of this method simply writes the plain text
221 221 representation of the object to ``io.Term.cout``. Subclasses should
222 222 override this method to send the entire `format_dict` to the
223 223 frontends.
224 224
225 225 Parameters
226 226 ----------
227 227 format_dict : dict
228 228 The format dict for the object passed to `sys.displayhook`.
229 229 """
230 230 # We want to print because we want to always make sure we have a
231 231 # newline, even if all the prompt separators are ''. This is the
232 232 # standard IPython behavior.
233 233 result_repr = format_dict['text/plain']
234 234 if '\n' in result_repr:
235 235 # So that multi-line strings line up with the left column of
236 236 # the screen, instead of having the output prompt mess up
237 237 # their first line.
238 238 # We use the ps_out_str template instead of the expanded prompt
239 239 # because the expansion may add ANSI escapes that will interfere
240 240 # with our ability to determine whether or not we should add
241 241 # a newline.
242 242 if self.ps_out_str and not self.ps_out_str.endswith('\n'):
243 243 # But avoid extraneous empty lines.
244 244 result_repr = '\n' + result_repr
245 245
246 246 print >>IPython.utils.io.Term.cout, result_repr
247 247
248 248 def update_user_ns(self, result):
249 249 """Update user_ns with various things like _, __, _1, etc."""
250 250
251 251 # Avoid recursive reference when displaying _oh/Out
252 252 if result is not self.shell.user_ns['_oh']:
253 253 if len(self.shell.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
254 254 warn('Output cache limit (currently '+
255 255 `self.cache_size`+' entries) hit.\n'
256 256 'Flushing cache and resetting history counter...\n'
257 257 'The only history variables available will be _,__,___ and _1\n'
258 258 'with the current result.')
259 259
260 260 self.flush()
261 261 # Don't overwrite '_' and friends if '_' is in __builtin__ (otherwise
262 262 # we cause buggy behavior for things like gettext).
263 263
264 264 if '_' not in __builtin__.__dict__:
265 265 self.___ = self.__
266 266 self.__ = self._
267 267 self._ = result
268 268 self.shell.user_ns.update({'_':self._,
269 269 '__':self.__,
270 270 '___':self.___})
271 271
272 272 # hackish access to top-level namespace to create _1,_2... dynamically
273 273 to_main = {}
274 274 if self.do_full_cache:
275 275 new_result = '_'+`self.prompt_count`
276 276 to_main[new_result] = result
277 277 self.shell.user_ns.update(to_main)
278 # This is a defaultdict of lists, so we can always append
279 self.shell.user_ns['_oh'][self.prompt_count].append(result)
278 self.shell.user_ns['_oh'][self.prompt_count] = result
280 279
281 280 def log_output(self, format_dict):
282 281 """Log the output."""
283 282 if self.shell.logger.log_output:
284 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'])
285 287
286 288 def finish_displayhook(self):
287 289 """Finish up all displayhook activities."""
288 290 IPython.utils.io.Term.cout.write(self.output_sep2)
289 291 IPython.utils.io.Term.cout.flush()
290 292
291 293 def __call__(self, result=None):
292 294 """Printing with history cache management.
293 295
294 296 This is invoked everytime the interpreter needs to print, and is
295 297 activated by setting the variable sys.displayhook to it.
296 298 """
297 299 self.check_for_underscore()
298 300 if result is not None and not self.quiet():
299 301 self.start_displayhook()
300 302 self.write_output_prompt()
301 303 format_dict = self.compute_format_data(result)
302 304 self.write_format_data(format_dict)
303 305 self.update_user_ns(result)
304 306 self.log_output(format_dict)
305 307 self.finish_displayhook()
306 308
307 309 def flush(self):
308 310 if not self.do_full_cache:
309 311 raise ValueError,"You shouldn't have reached the cache flush "\
310 312 "if full caching is not enabled!"
311 313 # delete auto-generated vars from global namespace
312 314
313 315 for n in range(1,self.prompt_count + 1):
314 316 key = '_'+`n`
315 317 try:
316 318 del self.shell.user_ns[key]
317 319 except: pass
318 320 self.shell.user_ns['_oh'].clear()
319 321
320 322 if '_' not in __builtin__.__dict__:
321 323 self.shell.user_ns.update({'_':None,'__':None, '___':None})
322 324 import gc
323 325 # TODO: Is this really needed?
324 326 gc.collect()
325 327
@@ -1,610 +1,617 b''
1 1 """ History related magics and functionality """
2 2 #-----------------------------------------------------------------------------
3 3 # Copyright (C) 2010 The IPython Development Team.
4 4 #
5 5 # Distributed under the terms of the BSD License.
6 6 #
7 7 # The full license is in the file COPYING.txt, distributed with this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13 from __future__ import print_function
14 14
15 15 # Stdlib imports
16 16 import datetime
17 17 import json
18 18 import os
19 19 import re
20 20 import sqlite3
21 21
22 22 from collections import defaultdict
23 23
24 24 # Our own packages
25 25 from IPython.config.configurable import Configurable
26 26 import IPython.utils.io
27 27
28 28 from IPython.testing import decorators as testdec
29 29 from IPython.utils.io import ask_yes_no
30 30 from IPython.utils.traitlets import Bool, Dict, Instance, Int, List, Unicode
31 31 from IPython.utils.warn import warn
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Classes and functions
35 35 #-----------------------------------------------------------------------------
36 36
37 37 class HistoryManager(Configurable):
38 38 """A class to organize all history-related functionality in one place.
39 39 """
40 40 # Public interface
41 41
42 42 # An instance of the IPython shell we are attached to
43 43 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
44 44 # Lists to hold processed and raw history. These start with a blank entry
45 45 # so that we can index them starting from 1
46 46 input_hist_parsed = List([""])
47 47 input_hist_raw = List([""])
48 48 # A list of directories visited during session
49 49 dir_hist = List()
50 # A dict of output history, keyed with ints from the shell's execution count
51 output_hist = Instance(defaultdict)
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
52 57 # String holding the path to the history file
53 58 hist_file = Unicode()
54 59 # The SQLite database
55 60 db = Instance(sqlite3.Connection)
56 61 # The number of the current session in the history database
57 62 session_number = Int()
58 63 # Should we log output to the database? (default no)
59 64 db_log_output = Bool(False, config=True)
60 65 # Write to database every x commands (higher values save disk access & power)
61 66 # Values of 1 or less effectively disable caching.
62 67 db_cache_size = Int(0, config=True)
63 68 # The input and output caches
64 69 db_input_cache = List()
65 70 db_output_cache = List()
66 71
67 72 # Private interface
68 73 # Variables used to store the three last inputs from the user. On each new
69 74 # history update, we populate the user's namespace with these, shifted as
70 75 # necessary.
71 76 _i00, _i, _ii, _iii = '','','',''
72 77
73 78 # A set with all forms of the exit command, so that we don't store them in
74 79 # the history (it's annoying to rewind the first entry and land on an exit
75 80 # call).
76 81 _exit_commands = None
77 82
78 83 def __init__(self, shell, config=None):
79 84 """Create a new history manager associated with a shell instance.
80 85 """
81 86 # We need a pointer back to the shell for various tasks.
82 87 super(HistoryManager, self).__init__(shell=shell, config=config)
83 88
84 89 # list of visited directories
85 90 try:
86 91 self.dir_hist = [os.getcwd()]
87 92 except OSError:
88 93 self.dir_hist = []
89 94
90 95 # Now the history file
91 96 if shell.profile:
92 97 histfname = 'history-%s' % shell.profile
93 98 else:
94 99 histfname = 'history'
95 100 self.hist_file = os.path.join(shell.ipython_dir, histfname + '.sqlite')
96 101 self.init_db()
97 102 self.new_session()
98 103
99 104 self._i00, self._i, self._ii, self._iii = '','','',''
100 self.output_hist = defaultdict(list)
105 self.output_hist_reprs = defaultdict(list)
101 106
102 107 self._exit_commands = set(['Quit', 'quit', 'Exit', 'exit', '%Quit',
103 108 '%quit', '%Exit', '%exit'])
104 109
105 110 def init_db(self):
106 111 """Connect to the database, and create tables if necessary."""
107 112 self.db = sqlite3.connect(self.hist_file)
108 113 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
109 114 primary key autoincrement, start timestamp,
110 115 end timestamp, num_cmds integer, remark text)""")
111 116 self.db.execute("""CREATE TABLE IF NOT EXISTS history
112 117 (session integer, line integer, source text, source_raw text,
113 118 PRIMARY KEY (session, line))""")
114 119 # Output history is optional, but ensure the table's there so it can be
115 120 # enabled later.
116 121 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
117 122 (session integer, line integer, output text,
118 123 PRIMARY KEY (session, line))""")
119 124 self.db.commit()
120 125
121 126 def new_session(self):
122 127 """Get a new session number."""
123 128 with self.db:
124 129 cur = self.db.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
125 130 NULL, "") """, (datetime.datetime.now(),))
126 131 self.session_number = cur.lastrowid
127 132
128 133 def end_session(self):
129 134 """Close the database session, filling in the end time and line count."""
130 135 self.writeout_cache()
131 136 with self.db:
132 137 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
133 138 session==?""", (datetime.datetime.now(),
134 139 len(self.input_hist_parsed)-1, self.session_number))
135 140 self.session_number = 0
136 141
137 142 def name_session(self, name):
138 143 """Give the current session a name in the history database."""
139 144 with self.db:
140 145 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
141 146 (name, self.session_number))
142 147
143 148 def reset(self, new_session=True):
144 149 """Clear the session history, releasing all object references, and
145 150 optionally open a new session."""
146 151 if self.session_number:
147 152 self.end_session()
148 153 self.input_hist_parsed[:] = [""]
149 154 self.input_hist_raw[:] = [""]
150 155 self.output_hist.clear()
151 156 # The directory history can't be completely empty
152 157 self.dir_hist[:] = [os.getcwd()]
153 158
154 159 if new_session:
155 160 self.new_session()
156 161
157 162 ## -------------------------------
158 163 ## Methods for retrieving history:
159 164 ## -------------------------------
160 165 def _get_hist_sql(self, sql, params, raw=True, output=False):
161 166 """Prepares and runs an SQL query for the history database.
162 167
163 168 Parameters
164 169 ----------
165 170 sql : str
166 171 Any filtering expressions to go after SELECT ... FROM ...
167 172 params : tuple
168 173 Parameters passed to the SQL query (to replace "?")
169 174 raw : bool
170 175 If True, get raw input.
171 176 output : bool
172 177 If True, include output where available.
173 178
174 179 Returns
175 180 -------
176 181 An iterator over 3-tuples: (session, line_number, command), or if output
177 182 is True, (session, line_number, (command, output)).
178 183 """
179 184 toget = 'source_raw' if raw else 'source'
180 185 sqlfrom = "history"
181 186 if output:
182 187 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
183 188 toget = "history.%s, output_history.output" % toget
184 189 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
185 190 (toget, sqlfrom) + sql, params)
186 191 if output: # Regroup into 3-tuples, and parse JSON
187 192 loads = lambda out: json.loads(out) if out else None
188 193 return ((ses, lin, (inp, loads(out))) \
189 194 for ses, lin, inp, out in cur)
190 195 return cur
191 196
192 197
193 198 def get_hist_tail(self, n=10, raw=True, output=False):
194 199 """Get the last n lines from the history database."""
195 200 self.writeout_cache()
196 201 cur = self._get_hist_sql("ORDER BY session DESC, line DESC LIMIT ?",
197 202 (n,), raw=raw, output=output)
198 203 return reversed(list(cur))
199 204
200 205 def get_hist_search(self, pattern="*", raw=True, output=False):
201 206 """Search the database using unix glob-style matching (wildcards * and
202 207 ?, escape using \).
203 208
204 209 Returns
205 210 -------
206 211 An iterator over tuples: (session, line_number, command)
207 212 """
208 213 tosearch = "source_raw" if raw else "source"
209 214 if output:
210 215 tosearch = "history." + tosearch
211 216 self.writeout_cache()
212 217 return self._get_hist_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
213 218 raw=raw, output=output)
214 219
215 220 def _get_hist_session(self, start=1, stop=None, raw=True, output=False):
216 221 """Get input and output history from the current session. Called by
217 222 get_history, and takes similar parameters."""
218 223 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
219 224
220 225 n = len(input_hist)
221 226 if start < 0:
222 227 start += n
223 228 if not stop:
224 229 stop = n
225 230 elif stop < 0:
226 231 stop += n
227 232
228 233 for i in range(start, stop):
229 234 if output:
230 output_item = [repr(x) for x in self.output_hist[i]]
231 line = (input_hist[i], output_item)
235 line = (input_hist[i], self.output_hist_reprs.get(i))
232 236 else:
233 237 line = input_hist[i]
234 238 yield (0, i, line)
235 239
236 240 def get_history(self, session=0, start=1, stop=None, raw=True,output=False):
237 241 """Retrieve input by session.
238 242
239 243 Parameters
240 244 ----------
241 245 session : int
242 246 Session number to retrieve. The current session is 0, and negative
243 247 numbers count back from current session, so -1 is previous session.
244 248 start : int
245 249 First line to retrieve.
246 250 stop : int
247 251 End of line range (excluded from output itself). If None, retrieve
248 252 to the end of the session.
249 253 raw : bool
250 254 If True, return untranslated input
251 255 output : bool
252 256 If True, attempt to include output. This will be 'real' Python
253 257 objects for the current session, or text reprs from previous
254 258 sessions if db_log_output was enabled at the time. Where no output
255 259 is found, None is used.
256 260
257 261 Returns
258 262 -------
259 263 An iterator over the desired lines. Each line is a 3-tuple, either
260 264 (session, line, input) if output is False, or
261 265 (session, line, (input, output)) if output is True.
262 266 """
263 267 if session == 0 or session==self.session_number: # Current session
264 268 return self._get_hist_session(start, stop, raw, output)
265 269 if session < 0:
266 270 session += self.session_number
267 271
268 272 if stop:
269 273 lineclause = "line >= ? AND line < ?"
270 274 params = (session, start, stop)
271 275 else:
272 276 lineclause = "line>=?"
273 277 params = (session, start)
274 278
275 279 return self._get_hist_sql("WHERE session==? AND %s""" % lineclause,
276 280 params, raw=raw, output=output)
277 281
278 282 def get_hist_from_rangestr(self, rangestr, raw=True, output=False):
279 283 """Get lines of history from a string of ranges, as used by magic
280 284 commands %hist, %save, %macro, etc."""
281 285 for sess, s, e in extract_hist_ranges(rangestr):
282 286 for line in self.get_history(sess, s, e, raw=raw, output=output):
283 287 yield line
284 288
285 289 ## ----------------------------
286 290 ## Methods for storing history:
287 291 ## ----------------------------
288 292 def store_inputs(self, line_num, source, source_raw=None):
289 293 """Store source and raw input in history and create input cache
290 294 variables _i*.
291 295
292 296 Parameters
293 297 ----------
294 298 line_num : int
295 299 The prompt number of this input.
296 300
297 301 source : str
298 302 Python input.
299 303
300 304 source_raw : str, optional
301 305 If given, this is the raw input without any IPython transformations
302 306 applied to it. If not given, ``source`` is used.
303 307 """
304 308 if source_raw is None:
305 309 source_raw = source
306 310
307 311 # do not store exit/quit commands
308 312 if source_raw.strip() in self._exit_commands:
309 313 return
310 314
311 315 self.input_hist_parsed.append(source.rstrip())
312 316 self.input_hist_raw.append(source_raw.rstrip())
313 317
314 318 self.db_input_cache.append((self.session_number, line_num,
315 319 source, source_raw))
316 320 # Trigger to flush cache and write to DB.
317 321 if len(self.db_input_cache) >= self.db_cache_size:
318 322 self.writeout_cache()
319 323
320 324 # update the auto _i variables
321 325 self._iii = self._ii
322 326 self._ii = self._i
323 327 self._i = self._i00
324 328 self._i00 = source_raw
325 329
326 330 # hackish access to user namespace to create _i1,_i2... dynamically
327 331 new_i = '_i%s' % line_num
328 332 to_main = {'_i': self._i,
329 333 '_ii': self._ii,
330 334 '_iii': self._iii,
331 335 new_i : self._i00 }
332 336 self.shell.user_ns.update(to_main)
333 337
334 338 def store_output(self, line_num):
335 if (not self.db_log_output) or not self.output_hist[line_num]:
339 """If database output logging is enabled, this saves all the
340 outputs from the indicated prompt number to the database. It's
341 called by run_cell after code has been executed."""
342 if (not self.db_log_output) or not self.output_hist_reprs[line_num]:
336 343 return
337 output = json.dumps([repr(x) for x in self.output_hist[line_num]])
344 output = json.dumps(self.output_hist_reprs[line_num])
338 345 db_row = (self.session_number, line_num, output)
339 346 if self.db_cache_size > 1:
340 347 self.db_output_cache.append(db_row)
341 348 else:
342 349 with self.db:
343 350 self.db.execute("INSERT INTO output_history VALUES (?,?,?)", db_row)
344 351
345 352 def writeout_cache(self):
346 353 #print(self.db_input_cache)
347 354 with self.db:
348 355 self.db.executemany("INSERT INTO history VALUES (?, ?, ?, ?)",
349 356 self.db_input_cache)
350 357 self.db.executemany("INSERT INTO output_history VALUES (?, ?, ?)",
351 358 self.db_output_cache)
352 359 self.db_input_cache = []
353 360 self.db_output_cache = []
354 361
355 362
356 363 # To match, e.g. ~5/8-~2/3
357 364 range_re = re.compile(r"""
358 365 ((?P<startsess>~?\d+)/)?
359 366 (?P<start>\d+) # Only the start line num is compulsory
360 367 ((?P<sep>[\-:])
361 368 ((?P<endsess>~?\d+)/)?
362 369 (?P<end>\d+))?
363 370 """, re.VERBOSE)
364 371
365 372 def extract_hist_ranges(ranges_str):
366 373 """Turn a string of history ranges into 3-tuples of (session, start, stop).
367 374
368 375 Examples
369 376 --------
370 377 list(extract_input_ranges("~8/5-~7/4 2"))
371 378 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
372 379 """
373 380 for range_str in ranges_str.split():
374 381 rmatch = range_re.match(range_str)
375 382 if not rmatch:
376 383 continue
377 384 start = int(rmatch.group("start"))
378 385 end = rmatch.group("end")
379 386 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
380 387 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
381 388 end += 1
382 389 startsess = rmatch.group("startsess") or "0"
383 390 endsess = rmatch.group("endsess") or startsess
384 391 startsess = int(startsess.replace("~","-"))
385 392 endsess = int(endsess.replace("~","-"))
386 393 assert endsess >= startsess
387 394
388 395 if endsess == startsess:
389 396 yield (startsess, start, end)
390 397 continue
391 398 # Multiple sessions in one range:
392 399 yield (startsess, start, None)
393 400 for sess in range(startsess+1, endsess):
394 401 yield (sess, 1, None)
395 402 yield (endsess, 1, end)
396 403
397 404 def _format_lineno(session, line):
398 405 """Helper function to format line numbers properly."""
399 406 if session == 0:
400 407 return str(line)
401 408 return "%s#%s" % (session, line)
402 409
403 410 @testdec.skip_doctest
404 411 def magic_history(self, parameter_s = ''):
405 412 """Print input history (_i<n> variables), with most recent last.
406 413
407 414 %history -> print at most 40 inputs (some may be multi-line)\\
408 415 %history n -> print at most n inputs\\
409 416 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
410 417
411 418 By default, input history is printed without line numbers so it can be
412 419 directly pasted into an editor.
413 420
414 421 With -n, each input's number <n> is shown, and is accessible as the
415 422 automatically generated variable _i<n> as well as In[<n>]. Multi-line
416 423 statements are printed starting at a new line for easy copy/paste.
417 424
418 425 Options:
419 426
420 427 -n: print line numbers for each input.
421 428 This feature is only available if numbered prompts are in use.
422 429
423 430 -o: also print outputs for each input.
424 431
425 432 -p: print classic '>>>' python prompts before each input. This is useful
426 433 for making documentation, and in conjunction with -o, for producing
427 434 doctest-ready output.
428 435
429 436 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
430 437
431 438 -t: print the 'translated' history, as IPython understands it. IPython
432 439 filters your input and converts it all into valid Python source before
433 440 executing it (things like magics or aliases are turned into function
434 441 calls, for example). With this option, you'll see the native history
435 442 instead of the user-entered version: '%cd /' will be seen as
436 443 'get_ipython().magic("%cd /")' instead of '%cd /'.
437 444
438 445 -g: treat the arg as a pattern to grep for in (full) history.
439 446 This includes the saved history (almost all commands ever written).
440 447 Use '%hist -g' to show full saved history (may be very long).
441 448
442 449 -l: get the last n lines from all sessions. Specify n as a single arg, or
443 450 the default is the last 10 lines.
444 451
445 452 -f FILENAME: instead of printing the output to the screen, redirect it to
446 453 the given file. The file is always overwritten, though IPython asks for
447 454 confirmation first if it already exists.
448 455
449 456 Examples
450 457 --------
451 458 ::
452 459
453 460 In [6]: %hist -n 4 6
454 461 4:a = 12
455 462 5:print a**2
456 463
457 464 """
458 465
459 466 if not self.shell.displayhook.do_full_cache:
460 467 print('This feature is only available if numbered prompts are in use.')
461 468 return
462 469 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
463 470
464 471 # For brevity
465 472 history_manager = self.shell.history_manager
466 473
467 474 def _format_lineno(session, line):
468 475 """Helper function to format line numbers properly."""
469 476 if session in (0, history_manager.session_number):
470 477 return str(line)
471 478 return "%s/%s" % (session, line)
472 479
473 480 # Check if output to specific file was requested.
474 481 try:
475 482 outfname = opts['f']
476 483 except KeyError:
477 484 outfile = IPython.utils.io.Term.cout # default
478 485 # We don't want to close stdout at the end!
479 486 close_at_end = False
480 487 else:
481 488 if os.path.exists(outfname):
482 489 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
483 490 print('Aborting.')
484 491 return
485 492
486 493 outfile = open(outfname,'w')
487 494 close_at_end = True
488 495
489 496 print_nums = 'n' in opts
490 497 get_output = 'o' in opts
491 498 pyprompts = 'p' in opts
492 499 # Raw history is the default
493 500 raw = not('t' in opts)
494 501
495 502 default_length = 40
496 503 pattern = None
497 504
498 505 if 'g' in opts: # Glob search
499 506 pattern = "*" + args + "*" if args else "*"
500 507 hist = history_manager.get_hist_search(pattern, raw=raw,
501 508 output=get_output)
502 509 elif 'l' in opts: # Get 'tail'
503 510 try:
504 511 n = int(args)
505 512 except ValueError, IndexError:
506 513 n = 10
507 514 hist = history_manager.get_hist_tail(n, raw=raw, output=get_output)
508 515 else:
509 516 if args: # Get history by ranges
510 517 hist = history_manager.get_hist_from_rangestr(args, raw, get_output)
511 518 else: # Just get history for the current session
512 519 hist = history_manager.get_history(raw=raw, output=get_output)
513 520
514 521 # We could be displaying the entire history, so let's not try to pull it
515 522 # into a list in memory. Anything that needs more space will just misalign.
516 523 width = 4
517 524
518 525 for session, lineno, inline in hist:
519 526 # Print user history with tabs expanded to 4 spaces. The GUI clients
520 527 # use hard tabs for easier usability in auto-indented code, but we want
521 528 # to produce PEP-8 compliant history for safe pasting into an editor.
522 529 if get_output:
523 530 inline, output = inline
524 531 inline = inline.expandtabs(4).rstrip()
525 532
526 533 multiline = "\n" in inline
527 534 line_sep = '\n' if multiline else ' '
528 535 if print_nums:
529 536 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
530 537 line_sep), file=outfile, end='')
531 538 if pyprompts:
532 539 print(">>> ", end="", file=outfile)
533 540 if multiline:
534 541 inline = "\n... ".join(inline.splitlines()) + "\n..."
535 542 print(inline, file=outfile)
536 543 if get_output and output:
537 544 print("\n".join(output), file=outfile)
538 545
539 546 if close_at_end:
540 547 outfile.close()
541 548
542 549
543 550 def magic_rep(self, arg):
544 551 r""" Repeat a command, or get command to input line for editing
545 552
546 553 - %rep (no arguments):
547 554
548 555 Place a string version of last computation result (stored in the special '_'
549 556 variable) to the next input prompt. Allows you to create elaborate command
550 557 lines without using copy-paste::
551 558
552 559 $ l = ["hei", "vaan"]
553 560 $ "".join(l)
554 561 ==> heivaan
555 562 $ %rep
556 563 $ heivaan_ <== cursor blinking
557 564
558 565 %rep 45
559 566
560 567 Place history line 45 to next input prompt. Use %hist to find out the
561 568 number.
562 569
563 570 %rep 1-4 6-7 3
564 571
565 572 Repeat the specified lines immediately. Input slice syntax is the same as
566 573 in %macro and %save.
567 574
568 575 %rep foo
569 576
570 577 Place the most recent line that has the substring "foo" to next input.
571 578 (e.g. 'svn ci -m foobar').
572 579 """
573 580
574 581 opts,args = self.parse_options(arg,'',mode='list')
575 582 if not args: # Last output
576 583 self.set_next_input(str(self.shell.user_ns["_"]))
577 584 return
578 585
579 586 arg = " ".join(args)
580 587 histlines = self.history_manager.get_hist_from_rangestr(arg, raw=False)
581 588 histlines = [x[2] for x in histlines]
582 589
583 590 if len(histlines) > 1: # Execute immediately
584 591 histlines = "\n".join(histlines)
585 592 print("=== Executing: ===")
586 593 print(histlines)
587 594 print("=== Output: ===")
588 595 self.run_source(histlines, symbol="exec")
589 596
590 597 elif len(histlines) == 1: # Editable input
591 598 self.set_next_input(histlines[0].rstrip())
592 599
593 600 else: # Search for term - editable input
594 601 histlines = self.history_manager.get_hist_search("*"+arg+"*")
595 602 for h in reversed([x[2] for x in histlines]):
596 603 if 'rep' in h:
597 604 continue
598 605 self.set_next_input(h.rstrip())
599 606 return
600 607 print("Not found in history:", arg)
601 608
602 609
603 610 def init_ipython(ip):
604 611 ip.define_magic("rep", magic_rep)
605 612 ip.define_magic("hist",magic_history) # Alternative name
606 613 ip.define_magic("history",magic_history)
607 614
608 615 # XXX - ipy_completers are in quarantine, need to be updated to new apis
609 616 #import ipy_completers
610 617 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
@@ -1,94 +1,94 b''
1 1 """Tests for the IPython tab-completion machinery.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Module imports
5 5 #-----------------------------------------------------------------------------
6 6
7 7 # stdlib
8 8 import os
9 9 import sys
10 10 import unittest
11 11
12 12 # third party
13 13 import nose.tools as nt
14 14
15 15 # our own packages
16 16 from IPython.utils.tempdir import TemporaryDirectory
17 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 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 30 ip.history_manager = HistoryManager(shell=ip)
31 31 ip.history_manager.hist_file = histfile
32 32 ip.history_manager.init_db() # Has to be called after changing file
33 33 ip.history_manager.reset()
34 34 print 'test',histfile
35 35 hist = ['a=1', 'def f():\n test = 1\n return test', 'b=2']
36 36 for i, h in enumerate(hist, start=1):
37 37 ip.history_manager.store_inputs(i, h)
38 38
39 39 ip.history_manager.db_log_output = True
40 40 # Doesn't match the input, but we'll just check it's stored.
41 ip.history_manager.output_hist[3].append("spam")
41 ip.history_manager.output_hist_reprs[3].append("spam")
42 42 ip.history_manager.store_output(3)
43 43
44 44 nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist)
45 45
46 46 # Check lines were written to DB
47 47 c = ip.history_manager.db.execute("SELECT source_raw FROM history")
48 48 nt.assert_equal([x for x, in c], hist)
49 49
50 50 # New session
51 51 ip.history_manager.reset()
52 52 newcmds = ["z=5","class X(object):\n pass", "k='p'"]
53 53 for i, cmd in enumerate(newcmds, start=1):
54 54 ip.history_manager.store_inputs(i, cmd)
55 55 gothist = ip.history_manager.get_history(start=1, stop=4)
56 56 nt.assert_equal(list(gothist), zip([0,0,0],[1,2,3], newcmds))
57 57 # Previous session:
58 58 gothist = ip.history_manager.get_history(-1, 1, 4)
59 59 nt.assert_equal(list(gothist), zip([1,1,1],[1,2,3], hist))
60 60
61 61 # Check get_hist_tail
62 62 gothist = ip.history_manager.get_hist_tail(4, output=True)
63 expected = [(1, 3, (hist[-1], [repr("spam")])),
63 expected = [(1, 3, (hist[-1], ["spam"])),
64 64 (2, 1, (newcmds[0], None)),
65 65 (2, 2, (newcmds[1], None)),
66 66 (2, 3, (newcmds[2], None)),]
67 67 nt.assert_equal(list(gothist), expected)
68 68
69 69 # Check get_hist_search
70 70 gothist = ip.history_manager.get_hist_search("*test*")
71 71 nt.assert_equal(list(gothist), [(1,2,hist[1])] )
72 72 gothist = ip.history_manager.get_hist_search("b*", output=True)
73 nt.assert_equal(list(gothist), [(1,3,(hist[2],[repr("spam")]))] )
73 nt.assert_equal(list(gothist), [(1,3,(hist[2],["spam"]))] )
74 74
75 75 # Cross testing: check that magic %save can get previous session.
76 76 testfilename = os.path.realpath(os.path.join(tmpdir, "test.py"))
77 77 ip.magic_save(testfilename + " ~1/1-3")
78 78 testfile = open(testfilename, "r")
79 79 nt.assert_equal(testfile.read(), "\n".join(hist))
80 80 finally:
81 81 # Restore history manager
82 82 ip.history_manager = hist_manager_ori
83 83
84 84 def test_extract_hist_ranges():
85 85 instr = "1 2/3 ~4/5-6 ~4/7-~4/9 ~9/2-~7/5"
86 86 expected = [(0, 1, 2), # 0 == current session
87 87 (2, 3, 4),
88 88 (-4, 5, 7),
89 89 (-4, 7, 10),
90 90 (-9, 2, None), # None == to end
91 91 (-8, 1, None),
92 92 (-7, 1, 6)]
93 93 actual = list(extract_hist_ranges(instr))
94 94 nt.assert_equal(actual, expected)
General Comments 0
You need to be logged in to leave comments. Login now