##// END OF EJS Templates
Fix writing history with output to a file in Python 2...
Thomas Kluyver -
Show More
@@ -1,318 +1,319 b''
1 1 """Implementation of magic functions related to History.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012, IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14 from __future__ import print_function
15 15
16 16 # Stdlib
17 17 import os
18 18 from io import open as io_open
19 19
20 20 # Our own packages
21 21 from IPython.core.error import StdinNotImplementedError
22 22 from IPython.core.magic import Magics, magics_class, line_magic
23 23 from IPython.core.magic_arguments import (argument, magic_arguments,
24 24 parse_argstring)
25 25 from IPython.testing.skipdoctest import skip_doctest
26 26 from IPython.utils import io
27 from IPython.utils.py3compat import cast_unicode_py2
27 28
28 29 #-----------------------------------------------------------------------------
29 30 # Magics class implementation
30 31 #-----------------------------------------------------------------------------
31 32
32 33
33 34 _unspecified = object()
34 35
35 36
36 37 @magics_class
37 38 class HistoryMagics(Magics):
38 39
39 40 @magic_arguments()
40 41 @argument(
41 42 '-n', dest='print_nums', action='store_true', default=False,
42 43 help="""
43 44 print line numbers for each input.
44 45 This feature is only available if numbered prompts are in use.
45 46 """)
46 47 @argument(
47 48 '-o', dest='get_output', action='store_true', default=False,
48 49 help="also print outputs for each input.")
49 50 @argument(
50 51 '-p', dest='pyprompts', action='store_true', default=False,
51 52 help="""
52 53 print classic '>>>' python prompts before each input.
53 54 This is useful for making documentation, and in conjunction
54 55 with -o, for producing doctest-ready output.
55 56 """)
56 57 @argument(
57 58 '-t', dest='raw', action='store_false', default=True,
58 59 help="""
59 60 print the 'translated' history, as IPython understands it.
60 61 IPython filters your input and converts it all into valid Python
61 62 source before executing it (things like magics or aliases are turned
62 63 into function calls, for example). With this option, you'll see the
63 64 native history instead of the user-entered version: '%%cd /' will be
64 65 seen as 'get_ipython().magic("%%cd /")' instead of '%%cd /'.
65 66 """)
66 67 @argument(
67 68 '-f', dest='filename',
68 69 help="""
69 70 FILENAME: instead of printing the output to the screen, redirect
70 71 it to the given file. The file is always overwritten, though *when
71 72 it can*, IPython asks for confirmation first. In particular, running
72 73 the command 'history -f FILENAME' from the IPython Notebook
73 74 interface will replace FILENAME even if it already exists *without*
74 75 confirmation.
75 76 """)
76 77 @argument(
77 78 '-g', dest='pattern', nargs='*', default=None,
78 79 help="""
79 80 treat the arg as a glob pattern to search for in (full) history.
80 81 This includes the saved history (almost all commands ever written).
81 82 The pattern may contain '?' to match one unknown character and '*'
82 83 to match any number of unknown characters. Use '%%hist -g' to show
83 84 full saved history (may be very long).
84 85 """)
85 86 @argument(
86 87 '-l', dest='limit', type=int, nargs='?', default=_unspecified,
87 88 help="""
88 89 get the last n lines from all sessions. Specify n as a single
89 90 arg, or the default is the last 10 lines.
90 91 """)
91 92 @argument(
92 93 '-u', dest='unique', action='store_true',
93 94 help="""
94 95 when searching history using `-g`, show only unique history.
95 96 """)
96 97 @argument('range', nargs='*')
97 98 @skip_doctest
98 99 @line_magic
99 100 def history(self, parameter_s = ''):
100 101 """Print input history (_i<n> variables), with most recent last.
101 102
102 103 By default, input history is printed without line numbers so it can be
103 104 directly pasted into an editor. Use -n to show them.
104 105
105 106 By default, all input history from the current session is displayed.
106 107 Ranges of history can be indicated using the syntax:
107 108
108 109 ``4``
109 110 Line 4, current session
110 111 ``4-6``
111 112 Lines 4-6, current session
112 113 ``243/1-5``
113 114 Lines 1-5, session 243
114 115 ``~2/7``
115 116 Line 7, session 2 before current
116 117 ``~8/1-~6/5``
117 118 From the first line of 8 sessions ago, to the fifth line of 6
118 119 sessions ago.
119 120
120 121 Multiple ranges can be entered, separated by spaces
121 122
122 123 The same syntax is used by %macro, %save, %edit, %rerun
123 124
124 125 Examples
125 126 --------
126 127 ::
127 128
128 129 In [6]: %history -n 4-6
129 130 4:a = 12
130 131 5:print a**2
131 132 6:%history -n 4-6
132 133
133 134 """
134 135
135 136 args = parse_argstring(self.history, parameter_s)
136 137
137 138 # For brevity
138 139 history_manager = self.shell.history_manager
139 140
140 141 def _format_lineno(session, line):
141 142 """Helper function to format line numbers properly."""
142 143 if session in (0, history_manager.session_number):
143 144 return str(line)
144 145 return "%s/%s" % (session, line)
145 146
146 147 # Check if output to specific file was requested.
147 148 outfname = args.filename
148 149 if not outfname:
149 150 outfile = io.stdout # default
150 151 # We don't want to close stdout at the end!
151 152 close_at_end = False
152 153 else:
153 154 if os.path.exists(outfname):
154 155 try:
155 156 ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
156 157 except StdinNotImplementedError:
157 158 ans = True
158 159 if not ans:
159 160 print('Aborting.')
160 161 return
161 162 print("Overwriting file.")
162 163 outfile = io_open(outfname, 'w', encoding='utf-8')
163 164 close_at_end = True
164 165
165 166 print_nums = args.print_nums
166 167 get_output = args.get_output
167 168 pyprompts = args.pyprompts
168 169 raw = args.raw
169 170
170 171 pattern = None
171 172 limit = None if args.limit is _unspecified else args.limit
172 173
173 174 if args.pattern is not None:
174 175 if args.pattern:
175 176 pattern = "*" + " ".join(args.pattern) + "*"
176 177 else:
177 178 pattern = "*"
178 179 hist = history_manager.search(pattern, raw=raw, output=get_output,
179 180 n=limit, unique=args.unique)
180 181 print_nums = True
181 182 elif args.limit is not _unspecified:
182 183 n = 10 if limit is None else limit
183 184 hist = history_manager.get_tail(n, raw=raw, output=get_output)
184 185 else:
185 186 if args.range: # Get history by ranges
186 187 hist = history_manager.get_range_by_str(" ".join(args.range),
187 188 raw, get_output)
188 189 else: # Just get history for the current session
189 190 hist = history_manager.get_range(raw=raw, output=get_output)
190 191
191 192 # We could be displaying the entire history, so let's not try to pull
192 193 # it into a list in memory. Anything that needs more space will just
193 194 # misalign.
194 195 width = 4
195 196
196 197 for session, lineno, inline in hist:
197 198 # Print user history with tabs expanded to 4 spaces. The GUI
198 199 # clients use hard tabs for easier usability in auto-indented code,
199 200 # but we want to produce PEP-8 compliant history for safe pasting
200 201 # into an editor.
201 202 if get_output:
202 203 inline, output = inline
203 204 inline = inline.expandtabs(4).rstrip()
204 205
205 206 multiline = "\n" in inline
206 207 line_sep = '\n' if multiline else ' '
207 208 if print_nums:
208 209 print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width),
209 210 line_sep), file=outfile, end=u'')
210 211 if pyprompts:
211 212 print(u">>> ", end=u"", file=outfile)
212 213 if multiline:
213 214 inline = "\n... ".join(inline.splitlines()) + "\n..."
214 215 print(inline, file=outfile)
215 216 if get_output and output:
216 print(output, file=outfile)
217 print(cast_unicode_py2(output), file=outfile)
217 218
218 219 if close_at_end:
219 220 outfile.close()
220 221
221 222 @line_magic
222 223 def recall(self, arg):
223 224 r"""Repeat a command, or get command to input line for editing.
224 225
225 226 %recall and %rep are equivalent.
226 227
227 228 - %recall (no arguments):
228 229
229 230 Place a string version of last computation result (stored in the
230 231 special '_' variable) to the next input prompt. Allows you to create
231 232 elaborate command lines without using copy-paste::
232 233
233 234 In[1]: l = ["hei", "vaan"]
234 235 In[2]: "".join(l)
235 236 Out[2]: heivaan
236 237 In[3]: %recall
237 238 In[4]: heivaan_ <== cursor blinking
238 239
239 240 %recall 45
240 241
241 242 Place history line 45 on the next input prompt. Use %hist to find
242 243 out the number.
243 244
244 245 %recall 1-4
245 246
246 247 Combine the specified lines into one cell, and place it on the next
247 248 input prompt. See %history for the slice syntax.
248 249
249 250 %recall foo+bar
250 251
251 252 If foo+bar can be evaluated in the user namespace, the result is
252 253 placed at the next input prompt. Otherwise, the history is searched
253 254 for lines which contain that substring, and the most recent one is
254 255 placed at the next input prompt.
255 256 """
256 257 if not arg: # Last output
257 258 self.shell.set_next_input(str(self.shell.user_ns["_"]))
258 259 return
259 260 # Get history range
260 261 histlines = self.shell.history_manager.get_range_by_str(arg)
261 262 cmd = "\n".join(x[2] for x in histlines)
262 263 if cmd:
263 264 self.shell.set_next_input(cmd.rstrip())
264 265 return
265 266
266 267 try: # Variable in user namespace
267 268 cmd = str(eval(arg, self.shell.user_ns))
268 269 except Exception: # Search for term in history
269 270 histlines = self.shell.history_manager.search("*"+arg+"*")
270 271 for h in reversed([x[2] for x in histlines]):
271 272 if 'recall' in h or 'rep' in h:
272 273 continue
273 274 self.shell.set_next_input(h.rstrip())
274 275 return
275 276 else:
276 277 self.shell.set_next_input(cmd.rstrip())
277 278 print("Couldn't evaluate or find in history:", arg)
278 279
279 280 @line_magic
280 281 def rerun(self, parameter_s=''):
281 282 """Re-run previous input
282 283
283 284 By default, you can specify ranges of input history to be repeated
284 285 (as with %history). With no arguments, it will repeat the last line.
285 286
286 287 Options:
287 288
288 289 -l <n> : Repeat the last n lines of input, not including the
289 290 current command.
290 291
291 292 -g foo : Repeat the most recent line which contains foo
292 293 """
293 294 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
294 295 if "l" in opts: # Last n lines
295 296 n = int(opts['l'])
296 297 hist = self.shell.history_manager.get_tail(n)
297 298 elif "g" in opts: # Search
298 299 p = "*"+opts['g']+"*"
299 300 hist = list(self.shell.history_manager.search(p))
300 301 for l in reversed(hist):
301 302 if "rerun" not in l[2]:
302 303 hist = [l] # The last match which isn't a %rerun
303 304 break
304 305 else:
305 306 hist = [] # No matches except %rerun
306 307 elif args: # Specify history ranges
307 308 hist = self.shell.history_manager.get_range_by_str(args)
308 309 else: # Last line
309 310 hist = self.shell.history_manager.get_tail(1)
310 311 hist = [x[2] for x in hist]
311 312 if not hist:
312 313 print("No lines in history match specification")
313 314 return
314 315 histlines = "\n".join(hist)
315 316 print("=== Executing: ===")
316 317 print(histlines)
317 318 print("=== Output: ===")
318 319 self.shell.run_cell("\n".join(hist), store_history=False)
General Comments 0
You need to be logged in to leave comments. Login now