##// END OF EJS Templates
Fix %history to stop printing last line....
Fernando Perez -
Show More
@@ -1,274 +1,276 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ History related magics and functionality """
3 3
4 4 # Stdlib imports
5 5 import fnmatch
6 6 import os
7 7
8 8 from IPython.utils.genutils import Term, ask_yes_no, warn
9 9 from IPython.core import ipapi
10 10
11 11 def magic_history(self, parameter_s = ''):
12 12 """Print input history (_i<n> variables), with most recent last.
13 13
14 14 %history -> print at most 40 inputs (some may be multi-line)\\
15 15 %history n -> print at most n inputs\\
16 16 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
17 17
18 18 By default, input history is printed without line numbers so it can be
19 19 directly pasted into an editor.
20 20
21 21 With -n, each input's number <n> is shown, and is accessible as the
22 22 automatically generated variable _i<n> as well as In[<n>]. Multi-line
23 23 statements are printed starting at a new line for easy copy/paste.
24 24
25 25 Options:
26 26
27 27 -n: print line numbers for each input.
28 28 This feature is only available if numbered prompts are in use.
29 29
30 30 -o: also print outputs for each input.
31 31
32 32 -p: print classic '>>>' python prompts before each input. This is useful
33 33 for making documentation, and in conjunction with -o, for producing
34 34 doctest-ready output.
35 35
36 36 -t: (default) print the 'translated' history, as IPython understands it.
37 37 IPython filters your input and converts it all into valid Python source
38 38 before executing it (things like magics or aliases are turned into
39 39 function calls, for example). With this option, you'll see the native
40 40 history instead of the user-entered version: '%cd /' will be seen as
41 41 '_ip.magic("%cd /")' instead of '%cd /'.
42 42
43 43 -r: print the 'raw' history, i.e. the actual commands you typed.
44 44
45 45 -g: treat the arg as a pattern to grep for in (full) history.
46 46 This includes the "shadow history" (almost all commands ever written).
47 47 Use '%hist -g' to show full shadow history (may be very long).
48 48 In shadow history, every index nuwber starts with 0.
49 49
50 50 -f FILENAME: instead of printing the output to the screen, redirect it to
51 51 the given file. The file is always overwritten, though IPython asks for
52 52 confirmation first if it already exists.
53 53 """
54 54
55 55 if not self.outputcache.do_full_cache:
56 56 print 'This feature is only available if numbered prompts are in use.'
57 57 return
58 58 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
59 59
60 60 # Check if output to specific file was requested.
61 61 try:
62 62 outfname = opts['f']
63 63 except KeyError:
64 64 outfile = Term.cout # default
65 65 # We don't want to close stdout at the end!
66 66 close_at_end = False
67 67 else:
68 68 if os.path.exists(outfname):
69 69 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
70 70 print 'Aborting.'
71 71 return
72 72
73 73 outfile = open(outfname,'w')
74 74 close_at_end = True
75 75
76 76 if 't' in opts:
77 77 input_hist = self.input_hist
78 78 elif 'r' in opts:
79 79 input_hist = self.input_hist_raw
80 80 else:
81 81 input_hist = self.input_hist
82 82
83 83 default_length = 40
84 84 pattern = None
85 85 if 'g' in opts:
86 86 init = 1
87 87 final = len(input_hist)
88 parts = parameter_s.split(None,1)
88 parts = parameter_s.split(None, 1)
89 89 if len(parts) == 1:
90 90 parts += '*'
91 91 head, pattern = parts
92 92 pattern = "*" + pattern + "*"
93 93 elif len(args) == 0:
94 final = len(input_hist)
94 final = len(input_hist)-1
95 95 init = max(1,final-default_length)
96 96 elif len(args) == 1:
97 97 final = len(input_hist)
98 init = max(1,final-int(args[0]))
98 init = max(1, final-int(args[0]))
99 99 elif len(args) == 2:
100 init,final = map(int,args)
100 init, final = map(int, args)
101 101 else:
102 102 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
103 print self.magic_hist.__doc__
103 print >> Term.cout, self.magic_hist.__doc__
104 104 return
105 105
106 106 width = len(str(final))
107 107 line_sep = ['','\n']
108 108 print_nums = 'n' in opts
109 109 print_outputs = 'o' in opts
110 110 pyprompts = 'p' in opts
111 111
112 112 found = False
113 113 if pattern is not None:
114 114 sh = self.shadowhist.all()
115 115 for idx, s in sh:
116 116 if fnmatch.fnmatch(s, pattern):
117 print "0%d: %s" %(idx, s)
117 print >> outfile, "0%d: %s" %(idx, s)
118 118 found = True
119 119
120 120 if found:
121 print "==="
122 print "shadow history ends, fetch by %rep <number> (must start with 0)"
123 print "=== start of normal history ==="
121 print >> outfile, "==="
122 print >> outfile, \
123 "shadow history ends, fetch by %rep <number> (must start with 0)"
124 print >> outfile, "=== start of normal history ==="
124 125
125 126 for in_num in range(init,final):
126 127 inline = input_hist[in_num]
127 128 if pattern is not None and not fnmatch.fnmatch(inline, pattern):
128 129 continue
129 130
130 131 multiline = int(inline.count('\n') > 1)
131 132 if print_nums:
132 133 print >> outfile, \
133 '%s:%s' % (str(in_num).ljust(width),line_sep[multiline]),
134 '%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
134 135 if pyprompts:
135 136 print >> outfile, '>>>',
136 137 if multiline:
137 138 lines = inline.splitlines()
138 139 print >> outfile, '\n... '.join(lines)
139 140 print >> outfile, '... '
140 141 else:
141 142 print >> outfile, inline,
142 143 else:
143 144 print >> outfile, inline,
144 output = self.shell.user_ns['Out'].get(in_num)
145 if output is not None:
146 print repr(output)
145 if print_outputs:
146 output = self.shell.user_ns['Out'].get(in_num)
147 if output is not None:
148 print >> outfile, repr(output)
147 149
148 150 if close_at_end:
149 151 outfile.close()
150 152
151 153
152 154 def magic_hist(self, parameter_s=''):
153 155 """Alternate name for %history."""
154 156 return self.magic_history(parameter_s)
155 157
156 158
157 159 def rep_f(self, arg):
158 160 r""" Repeat a command, or get command to input line for editing
159 161
160 162 - %rep (no arguments):
161 163
162 164 Place a string version of last computation result (stored in the special '_'
163 165 variable) to the next input prompt. Allows you to create elaborate command
164 166 lines without using copy-paste::
165 167
166 168 $ l = ["hei", "vaan"]
167 169 $ "".join(l)
168 170 ==> heivaan
169 171 $ %rep
170 172 $ heivaan_ <== cursor blinking
171 173
172 174 %rep 45
173 175
174 176 Place history line 45 to next input prompt. Use %hist to find out the
175 177 number.
176 178
177 179 %rep 1-4 6-7 3
178 180
179 181 Repeat the specified lines immediately. Input slice syntax is the same as
180 182 in %macro and %save.
181 183
182 184 %rep foo
183 185
184 186 Place the most recent line that has the substring "foo" to next input.
185 187 (e.g. 'svn ci -m foobar').
186 188 """
187 189
188 190 opts,args = self.parse_options(arg,'',mode='list')
189 191 if not args:
190 192 self.set_next_input(str(self.user_ns["_"]))
191 193 return
192 194
193 195 if len(args) == 1 and not '-' in args[0]:
194 196 arg = args[0]
195 197 if len(arg) > 1 and arg.startswith('0'):
196 198 # get from shadow hist
197 199 num = int(arg[1:])
198 200 line = self.shadowhist.get(num)
199 201 self.set_next_input(str(line))
200 202 return
201 203 try:
202 204 num = int(args[0])
203 205 self.set_next_input(str(self.input_hist_raw[num]).rstrip())
204 206 return
205 207 except ValueError:
206 208 pass
207 209
208 210 for h in reversed(self.input_hist_raw):
209 211 if 'rep' in h:
210 212 continue
211 213 if fnmatch.fnmatch(h,'*' + arg + '*'):
212 214 self.set_next_input(str(h).rstrip())
213 215 return
214 216
215 217 try:
216 218 lines = self.extract_input_slices(args, True)
217 219 print "lines",lines
218 220 self.runlines(lines)
219 221 except ValueError:
220 222 print "Not found in recent history:", args
221 223
222 224
223 225 _sentinel = object()
224 226
225 227 class ShadowHist(object):
226 228 def __init__(self,db):
227 229 # cmd => idx mapping
228 230 self.curidx = 0
229 231 self.db = db
230 232 self.disabled = False
231 233
232 234 def inc_idx(self):
233 235 idx = self.db.get('shadowhist_idx', 1)
234 236 self.db['shadowhist_idx'] = idx + 1
235 237 return idx
236 238
237 239 def add(self, ent):
238 240 if self.disabled:
239 241 return
240 242 try:
241 243 old = self.db.hget('shadowhist', ent, _sentinel)
242 244 if old is not _sentinel:
243 245 return
244 246 newidx = self.inc_idx()
245 247 #print "new",newidx # dbg
246 248 self.db.hset('shadowhist',ent, newidx)
247 249 except:
248 250 ipapi.get().showtraceback()
249 251 print "WARNING: disabling shadow history"
250 252 self.disabled = True
251 253
252 254 def all(self):
253 255 d = self.db.hdict('shadowhist')
254 256 items = [(i,s) for (s,i) in d.items()]
255 257 items.sort()
256 258 return items
257 259
258 260 def get(self, idx):
259 261 all = self.all()
260 262
261 263 for k, v in all:
262 264 #print k,v
263 265 if k == idx:
264 266 return v
265 267
266 268
267 269 def init_ipython(ip):
268 270 ip.define_magic("rep",rep_f)
269 271 ip.define_magic("hist",magic_hist)
270 272 ip.define_magic("history",magic_history)
271 273
272 274 # XXX - ipy_completers are in quarantine, need to be updated to new apis
273 275 #import ipy_completers
274 276 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
@@ -1,279 +1,278 b''
1 1 """Tests for various magic functions.
2 2
3 3 Needs to be run by nose (to make ipython session available).
4 4 """
5 5 from __future__ import absolute_import
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Imports
9 9 #-----------------------------------------------------------------------------
10 10
11 11 # stdlib
12 12 import os
13 13 import sys
14 14 import tempfile
15 15 import types
16 16 from cStringIO import StringIO
17 17
18 18 # third-party
19 19 import nose.tools as nt
20 20
21 21 # our own
22 22 from IPython.utils import genutils
23 23 from IPython.utils.platutils import find_cmd, get_long_path_name
24 24 from IPython.testing import decorators as dec
25 25 from IPython.testing import tools as tt
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Test functions begin
29 29 #-----------------------------------------------------------------------------
30 30 def test_rehashx():
31 31 # clear up everything
32 32 _ip = get_ipython()
33 33 _ip.alias_manager.alias_table.clear()
34 34 del _ip.db['syscmdlist']
35 35
36 36 _ip.magic('rehashx')
37 37 # Practically ALL ipython development systems will have more than 10 aliases
38 38
39 39 yield (nt.assert_true, len(_ip.alias_manager.alias_table) > 10)
40 40 for key, val in _ip.alias_manager.alias_table.items():
41 41 # we must strip dots from alias names
42 42 nt.assert_true('.' not in key)
43 43
44 44 # rehashx must fill up syscmdlist
45 45 scoms = _ip.db['syscmdlist']
46 46 yield (nt.assert_true, len(scoms) > 10)
47 47
48 48
49 49 def test_magic_parse_options():
50 50 """Test that we don't mangle paths when parsing magic options."""
51 51 ip = get_ipython()
52 52 path = 'c:\\x'
53 53 opts = ip.parse_options('-f %s' % path,'f:')[0]
54 54 # argv splitting is os-dependent
55 55 if os.name == 'posix':
56 56 expected = 'c:x'
57 57 else:
58 58 expected = path
59 59 nt.assert_equals(opts['f'], expected)
60 60
61 61
62 62 def doctest_hist_f():
63 63 """Test %hist -f with temporary filename.
64 64
65 65 In [9]: import tempfile
66 66
67 67 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
68 68
69 69 In [11]: %hist -n -f $tfile 3
70 70
71 71 In [13]: import os; os.unlink(tfile)
72 72 """
73 73
74 74
75 75 def doctest_hist_r():
76 76 """Test %hist -r
77 77
78 78 XXX - This test is not recording the output correctly. For some reason, in
79 79 testing mode the raw history isn't getting populated. No idea why.
80 80 Disabling the output checking for now, though at least we do run it.
81 81
82 82 In [1]: 'hist' in _ip.lsmagic()
83 83 Out[1]: True
84 84
85 85 In [2]: x=1
86 86
87 87 In [3]: %hist -r 2
88 88 x=1 # random
89 89 %hist -r 2
90 90 """
91 91
92 92 def doctest_hist_op():
93 93 """Test %hist -op
94 94
95 95 In [1]: class b:
96 96 ...: pass
97 97 ...:
98 98
99 99 In [2]: class s(b):
100 100 ...: def __str__(self):
101 101 ...: return 's'
102 102 ...:
103 103
104 104 In [3]:
105 105
106 106 In [4]: class r(b):
107 107 ...: def __repr__(self):
108 108 ...: return 'r'
109 109 ...:
110 110
111 111 In [5]: class sr(s,r): pass
112 112 ...:
113 113
114 114 In [6]:
115 115
116 116 In [7]: bb=b()
117 117
118 118 In [8]: ss=s()
119 119
120 120 In [9]: rr=r()
121 121
122 122 In [10]: ssrr=sr()
123 123
124 124 In [11]: bb
125 125 Out[11]: <...b instance at ...>
126 126
127 127 In [12]: ss
128 128 Out[12]: <...s instance at ...>
129 129
130 130 In [13]:
131 131
132 132 In [14]: %hist -op
133 133 >>> class b:
134 134 ... pass
135 135 ...
136 136 >>> class s(b):
137 137 ... def __str__(self):
138 138 ... return 's'
139 139 ...
140 140 >>>
141 141 >>> class r(b):
142 142 ... def __repr__(self):
143 143 ... return 'r'
144 144 ...
145 145 >>> class sr(s,r): pass
146 146 >>>
147 147 >>> bb=b()
148 148 >>> ss=s()
149 149 >>> rr=r()
150 150 >>> ssrr=sr()
151 151 >>> bb
152 152 <...b instance at ...>
153 153 >>> ss
154 154 <...s instance at ...>
155 155 >>>
156 >>> get_ipython().magic("hist -op")
157 156 """
158 157
159 158 def test_shist():
160 159 # Simple tests of ShadowHist class - test generator.
161 160 import os, shutil, tempfile
162 161
163 162 from IPython.utils import pickleshare
164 163 from IPython.core.history import ShadowHist
165 164
166 165 tfile = tempfile.mktemp('','tmp-ipython-')
167 166
168 167 db = pickleshare.PickleShareDB(tfile)
169 168 s = ShadowHist(db)
170 169 s.add('hello')
171 170 s.add('world')
172 171 s.add('hello')
173 172 s.add('hello')
174 173 s.add('karhu')
175 174
176 175 yield nt.assert_equals,s.all(),[(1, 'hello'), (2, 'world'), (3, 'karhu')]
177 176
178 177 yield nt.assert_equal,s.get(2),'world'
179 178
180 179 shutil.rmtree(tfile)
181 180
182 181
183 182 # XXX failing for now, until we get clearcmd out of quarantine. But we should
184 183 # fix this and revert the skip to happen only if numpy is not around.
185 184 #@dec.skipif_not_numpy
186 185 @dec.skipknownfailure
187 186 def test_numpy_clear_array_undec():
188 187 from IPython.extensions import clearcmd
189 188
190 189 _ip.ex('import numpy as np')
191 190 _ip.ex('a = np.empty(2)')
192 191 yield (nt.assert_true, 'a' in _ip.user_ns)
193 192 _ip.magic('clear array')
194 193 yield (nt.assert_false, 'a' in _ip.user_ns)
195 194
196 195
197 196 # Multiple tests for clipboard pasting
198 197 @dec.parametric
199 198 def test_paste():
200 199 _ip = get_ipython()
201 200 def paste(txt, flags='-q'):
202 201 """Paste input text, by default in quiet mode"""
203 202 hooks.clipboard_get = lambda : txt
204 203 _ip.magic('paste '+flags)
205 204
206 205 # Inject fake clipboard hook but save original so we can restore it later
207 206 hooks = _ip.hooks
208 207 user_ns = _ip.user_ns
209 208 original_clip = hooks.clipboard_get
210 209
211 210 try:
212 211 # This try/except with an emtpy except clause is here only because
213 212 # try/yield/finally is invalid syntax in Python 2.4. This will be
214 213 # removed when we drop 2.4-compatibility, and the emtpy except below
215 214 # will be changed to a finally.
216 215
217 216 # Run tests with fake clipboard function
218 217 user_ns.pop('x', None)
219 218 paste('x=1')
220 219 yield nt.assert_equal(user_ns['x'], 1)
221 220
222 221 user_ns.pop('x', None)
223 222 paste('>>> x=2')
224 223 yield nt.assert_equal(user_ns['x'], 2)
225 224
226 225 paste("""
227 226 >>> x = [1,2,3]
228 227 >>> y = []
229 228 >>> for i in x:
230 229 ... y.append(i**2)
231 230 ...
232 231 """)
233 232 yield nt.assert_equal(user_ns['x'], [1,2,3])
234 233 yield nt.assert_equal(user_ns['y'], [1,4,9])
235 234
236 235 # Now, test that paste -r works
237 236 user_ns.pop('x', None)
238 237 yield nt.assert_false('x' in user_ns)
239 238 _ip.magic('paste -r')
240 239 yield nt.assert_equal(user_ns['x'], [1,2,3])
241 240
242 241 # Also test paste echoing, by temporarily faking the writer
243 242 w = StringIO()
244 243 writer = _ip.write
245 244 _ip.write = w.write
246 245 code = """
247 246 a = 100
248 247 b = 200"""
249 248 try:
250 249 paste(code,'')
251 250 out = w.getvalue()
252 251 finally:
253 252 _ip.write = writer
254 253 yield nt.assert_equal(user_ns['a'], 100)
255 254 yield nt.assert_equal(user_ns['b'], 200)
256 255 yield nt.assert_equal(out, code+"\n## -- End pasted text --\n")
257 256
258 257 finally:
259 258 # This should be in a finally clause, instead of the bare except above.
260 259 # Restore original hook
261 260 hooks.clipboard_get = original_clip
262 261
263 262
264 263 def test_time():
265 264 _ip.magic('time None')
266 265
267 266
268 267 def doctest_time():
269 268 """
270 269 In [10]: %time None
271 270 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
272 271 Wall time: 0.00 s
273 272 """
274 273
275 274 def test_doctest_mode():
276 275 "Toggle doctest_mode twice, it should be a no-op and run without error"
277 276 _ip.magic('doctest_mode')
278 277 _ip.magic('doctest_mode')
279 278
General Comments 0
You need to be logged in to leave comments. Login now