Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/IPython/core/magics/history.py: 20%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

153 statements  

1"""Implementation of magic functions related to History. 

2""" 

3#----------------------------------------------------------------------------- 

4# Copyright (c) 2012, IPython Development Team. 

5# 

6# Distributed under the terms of the Modified BSD License. 

7# 

8# The full license is in the file COPYING.txt, distributed with this software. 

9#----------------------------------------------------------------------------- 

10 

11#----------------------------------------------------------------------------- 

12# Imports 

13#----------------------------------------------------------------------------- 

14 

15# Stdlib 

16import os 

17import sys 

18from io import open as io_open 

19import fnmatch 

20 

21# Our own packages 

22from IPython.core.error import StdinNotImplementedError 

23from IPython.core.magic import Magics, magics_class, line_magic 

24from IPython.core.magic_arguments import (argument, magic_arguments, 

25 parse_argstring) 

26from IPython.testing.skipdoctest import skip_doctest 

27from IPython.utils import io 

28 

29#----------------------------------------------------------------------------- 

30# Magics class implementation 

31#----------------------------------------------------------------------------- 

32 

33 

34_unspecified = object() 

35 

36 

37@magics_class 

38class HistoryMagics(Magics): 

39 

40 @magic_arguments() 

41 @argument( 

42 '-n', dest='print_nums', action='store_true', default=False, 

43 help=""" 

44 print line numbers for each input. 

45 This feature is only available if numbered prompts are in use. 

46 """) 

47 @argument( 

48 '-o', dest='get_output', action='store_true', default=False, 

49 help="also print outputs for each input.") 

50 @argument( 

51 '-p', dest='pyprompts', action='store_true', default=False, 

52 help=""" 

53 print classic '>>>' python prompts before each input. 

54 This is useful for making documentation, and in conjunction 

55 with -o, for producing doctest-ready output. 

56 """) 

57 @argument( 

58 '-t', dest='raw', action='store_false', default=True, 

59 help=""" 

60 print the 'translated' history, as IPython understands it. 

61 IPython filters your input and converts it all into valid Python 

62 source before executing it (things like magics or aliases are turned 

63 into function calls, for example). With this option, you'll see the 

64 native history instead of the user-entered version: '%%cd /' will be 

65 seen as 'get_ipython().run_line_magic("cd", "/")' instead of '%%cd /'. 

66 """) 

67 @argument( 

68 '-f', dest='filename', 

69 help=""" 

70 FILENAME: instead of printing the output to the screen, redirect 

71 it to the given file. The file is always overwritten, though *when 

72 it can*, IPython asks for confirmation first. In particular, running 

73 the command 'history -f FILENAME' from the IPython Notebook 

74 interface will replace FILENAME even if it already exists *without* 

75 confirmation. 

76 """, 

77 ) 

78 @argument( 

79 "-y", 

80 dest="overwrite", 

81 help="yes, overwrite filename even if exists", 

82 action="store_true", 

83 default=None, 

84 ) 

85 @argument( 

86 "-g", 

87 dest="pattern", 

88 nargs="*", 

89 default=None, 

90 help=""" 

91 treat the arg as a glob pattern to search for in (full) history. 

92 This includes the saved history (almost all commands ever written). 

93 The pattern may contain '?' to match one unknown character and '*' 

94 to match any number of unknown characters. Use '%%hist -g' to show 

95 full saved history (may be very long). 

96 """) 

97 @argument( 

98 '-l', dest='limit', type=int, nargs='?', default=_unspecified, 

99 help=""" 

100 get the last n lines from all sessions. Specify n as a single 

101 arg, or the default is the last 10 lines. 

102 """) 

103 @argument( 

104 '-u', dest='unique', action='store_true', 

105 help=""" 

106 when searching history using `-g`, show only unique history. 

107 """) 

108 @argument('range', nargs='*') 

109 @skip_doctest 

110 @line_magic 

111 def history(self, parameter_s = ''): 

112 """Print input history (_i<n> variables), with most recent last. 

113 

114 By default, input history is printed without line numbers so it can be 

115 directly pasted into an editor. Use -n to show them. 

116 

117 By default, all input history from the current session is displayed. 

118 Ranges of history can be indicated using the syntax: 

119 

120 ``4`` 

121 Line 4, current session 

122 ``4-6`` 

123 Lines 4-6, current session 

124 ``4-`` 

125 Lines 4 onward (to end), current session 

126 ``243/1-5`` 

127 Lines 1-5, session 243 

128 ``~2/`` 

129 All lines of session 2 before current 

130 ``~4/4-`` 

131 Lines 4 onward (to end), session 4 before current 

132 ``~2/7`` 

133 Line 7, session 2 before current 

134 ``~8/1-~6/5`` 

135 From the first line of 8 sessions ago, to the fifth line of 6 

136 sessions ago. 

137 

138 Multiple ranges can be entered, separated by spaces 

139 

140 The same syntax is used by %macro, %save, %edit, %rerun 

141 

142 Examples 

143 -------- 

144 :: 

145 

146 In [6]: %history -n 4-6 

147 4:a = 12 

148 5:print(a**2) 

149 6:%history -n 4-6 

150 

151 """ 

152 

153 args = parse_argstring(self.history, parameter_s) 

154 

155 # For brevity 

156 history_manager = self.shell.history_manager 

157 

158 def _format_lineno(session, line): 

159 """Helper function to format line numbers properly.""" 

160 if session in (0, history_manager.session_number): 

161 return str(line) 

162 return "%s/%s" % (session, line) 

163 

164 # Check if output to specific file was requested. 

165 outfname = args.filename 

166 if not outfname: 

167 outfile = sys.stdout # default 

168 # We don't want to close stdout at the end! 

169 close_at_end = False 

170 else: 

171 outfname = os.path.expanduser(outfname) 

172 if args.overwrite is True: 

173 ans = True 

174 elif os.path.exists(outfname): 

175 ans = True 

176 if sys.stdin.isatty(): 

177 ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname) 

178 else: 

179 try: 

180 ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname) 

181 except StdinNotImplementedError: 

182 ans = True 

183 if not ans: 

184 print("Aborting.") 

185 return 

186 print("Overwriting file.") 

187 outfile = io_open(outfname, 'w', encoding='utf-8') 

188 close_at_end = True 

189 

190 print_nums = args.print_nums 

191 get_output = args.get_output 

192 pyprompts = args.pyprompts 

193 raw = args.raw 

194 

195 pattern = None 

196 limit = None if args.limit is _unspecified else args.limit 

197 

198 range_pattern = False 

199 if args.pattern is not None and not args.range: 

200 if args.pattern: 

201 pattern = "*" + " ".join(args.pattern) + "*" 

202 else: 

203 pattern = "*" 

204 hist = history_manager.search(pattern, raw=raw, output=get_output, 

205 n=limit, unique=args.unique) 

206 print_nums = True 

207 elif args.limit is not _unspecified: 

208 n = 10 if limit is None else limit 

209 hist = history_manager.get_tail(n, raw=raw, output=get_output) 

210 else: 

211 if args.pattern: 

212 range_pattern = "*" + " ".join(args.pattern) + "*" 

213 print_nums = True 

214 hist = history_manager.get_range_by_str( 

215 " ".join(args.range), raw, get_output 

216 ) 

217 

218 # We could be displaying the entire history, so let's not try to pull 

219 # it into a list in memory. Anything that needs more space will just 

220 # misalign. 

221 width = 4 

222 

223 for session, lineno, inline in hist: 

224 # Print user history with tabs expanded to 4 spaces. The GUI 

225 # clients use hard tabs for easier usability in auto-indented code, 

226 # but we want to produce PEP-8 compliant history for safe pasting 

227 # into an editor. 

228 if get_output: 

229 inline, output = inline 

230 if range_pattern: 

231 if not fnmatch.fnmatch(inline, range_pattern): 

232 continue 

233 inline = inline.expandtabs(4).rstrip() 

234 

235 multiline = "\n" in inline 

236 line_sep = '\n' if multiline else ' ' 

237 if print_nums: 

238 print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width), 

239 line_sep), file=outfile, end=u'') 

240 if pyprompts: 

241 print(u">>> ", end=u"", file=outfile) 

242 if multiline: 

243 inline = "\n... ".join(inline.splitlines()) + "\n..." 

244 print(inline, file=outfile) 

245 if get_output and output: 

246 print(output, file=outfile) 

247 

248 if close_at_end: 

249 outfile.close() 

250 

251 @line_magic 

252 def recall(self, arg): 

253 r"""Repeat a command, or get command to input line for editing. 

254 

255 %recall and %rep are equivalent. 

256 

257 - %recall (no arguments): 

258 

259 Place a string version of last computation result (stored in the 

260 special '_' variable) to the next input prompt. Allows you to create 

261 elaborate command lines without using copy-paste:: 

262 

263 In[1]: l = ["hei", "vaan"] 

264 In[2]: "".join(l) 

265 Out[2]: heivaan 

266 In[3]: %recall 

267 In[4]: heivaan_ <== cursor blinking 

268 

269 %recall 45 

270 

271 Place history line 45 on the next input prompt. Use %hist to find 

272 out the number. 

273 

274 %recall 1-4 

275 

276 Combine the specified lines into one cell, and place it on the next 

277 input prompt. See %history for the slice syntax. 

278 

279 %recall foo+bar 

280 

281 If foo+bar can be evaluated in the user namespace, the result is 

282 placed at the next input prompt. Otherwise, the history is searched 

283 for lines which contain that substring, and the most recent one is 

284 placed at the next input prompt. 

285 """ 

286 if not arg: # Last output 

287 self.shell.set_next_input(str(self.shell.user_ns["_"])) 

288 return 

289 # Get history range 

290 histlines = self.shell.history_manager.get_range_by_str(arg) 

291 cmd = "\n".join(x[2] for x in histlines) 

292 if cmd: 

293 self.shell.set_next_input(cmd.rstrip()) 

294 return 

295 

296 try: # Variable in user namespace 

297 cmd = str(eval(arg, self.shell.user_ns)) 

298 except Exception: # Search for term in history 

299 histlines = self.shell.history_manager.search("*"+arg+"*") 

300 for h in reversed([x[2] for x in histlines]): 

301 if 'recall' in h or 'rep' in h: 

302 continue 

303 self.shell.set_next_input(h.rstrip()) 

304 return 

305 else: 

306 self.shell.set_next_input(cmd.rstrip()) 

307 return 

308 print("Couldn't evaluate or find in history:", arg) 

309 

310 @line_magic 

311 def rerun(self, parameter_s=''): 

312 """Re-run previous input 

313 

314 By default, you can specify ranges of input history to be repeated 

315 (as with %history). With no arguments, it will repeat the last line. 

316 

317 Options: 

318 

319 -l <n> : Repeat the last n lines of input, not including the 

320 current command. 

321 

322 -g foo : Repeat the most recent line which contains foo 

323 """ 

324 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string') 

325 if "l" in opts: # Last n lines 

326 try: 

327 n = int(opts["l"]) 

328 except ValueError: 

329 print("Number of lines must be an integer") 

330 return 

331 

332 if n == 0: 

333 print("Requested 0 last lines - nothing to run") 

334 return 

335 elif n < 0: 

336 print("Number of lines to rerun cannot be negative") 

337 return 

338 

339 hist = self.shell.history_manager.get_tail(n) 

340 elif "g" in opts: # Search 

341 p = "*"+opts['g']+"*" 

342 hist = list(self.shell.history_manager.search(p)) 

343 for l in reversed(hist): 

344 if "rerun" not in l[2]: 

345 hist = [l] # The last match which isn't a %rerun 

346 break 

347 else: 

348 hist = [] # No matches except %rerun 

349 elif args: # Specify history ranges 

350 hist = self.shell.history_manager.get_range_by_str(args) 

351 else: # Last line 

352 hist = self.shell.history_manager.get_tail(1) 

353 hist = [x[2] for x in hist] 

354 if not hist: 

355 print("No lines in history match specification") 

356 return 

357 histlines = "\n".join(hist) 

358 print("=== Executing: ===") 

359 print(histlines) 

360 print("=== Output: ===") 

361 self.shell.run_cell("\n".join(hist), store_history=False)