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