##// END OF EJS Templates
Merge pull request #2404 from tkf/history-magic-search-limit...
Bussonnier Matthias -
r8686:fb4a45e8 merge
parent child Browse files
Show More
@@ -1,222 +1,241 b''
1 1 ''' A decorator-based method of constructing IPython magics with `argparse`
2 2 option handling.
3 3
4 4 New magic functions can be defined like so::
5 5
6 6 from IPython.core.magic_arguments import (argument, magic_arguments,
7 7 parse_argstring)
8 8
9 9 @magic_arguments()
10 10 @argument('-o', '--option', help='An optional argument.')
11 11 @argument('arg', type=int, help='An integer positional argument.')
12 12 def magic_cool(self, arg):
13 13 """ A really cool magic command.
14 14
15 15 """
16 16 args = parse_argstring(magic_cool, arg)
17 17 ...
18 18
19 19 The `@magic_arguments` decorator marks the function as having argparse arguments.
20 20 The `@argument` decorator adds an argument using the same syntax as argparse's
21 21 `add_argument()` method. More sophisticated uses may also require the
22 22 `@argument_group` or `@kwds` decorator to customize the formatting and the
23 23 parsing.
24 24
25 25 Help text for the magic is automatically generated from the docstring and the
26 26 arguments::
27 27
28 28 In[1]: %cool?
29 29 %cool [-o OPTION] arg
30 30
31 31 A really cool magic command.
32 32
33 33 positional arguments:
34 34 arg An integer positional argument.
35 35
36 36 optional arguments:
37 37 -o OPTION, --option OPTION
38 38 An optional argument.
39 39
40 40 '''
41 41 #-----------------------------------------------------------------------------
42 42 # Copyright (C) 2010-2011, IPython Development Team.
43 43 #
44 44 # Distributed under the terms of the Modified BSD License.
45 45 #
46 46 # The full license is in the file COPYING.txt, distributed with this software.
47 47 #-----------------------------------------------------------------------------
48 48
49 49 # Our own imports
50 50 from IPython.external import argparse
51 51 from IPython.core.error import UsageError
52 52 from IPython.utils.process import arg_split
53 53 from IPython.utils.text import dedent
54 54
55 55 class MagicHelpFormatter(argparse.RawDescriptionHelpFormatter):
56 56 """ A HelpFormatter which dedents but otherwise preserves indentation.
57 57 """
58 58 def _fill_text(self, text, width, indent):
59 59 return argparse.RawDescriptionHelpFormatter._fill_text(self, dedent(text), width, indent)
60 60
61 61 class MagicArgumentParser(argparse.ArgumentParser):
62 62 """ An ArgumentParser tweaked for use by IPython magics.
63 63 """
64 64 def __init__(self,
65 65 prog=None,
66 66 usage=None,
67 67 description=None,
68 68 epilog=None,
69 69 parents=None,
70 70 formatter_class=MagicHelpFormatter,
71 71 prefix_chars='-',
72 72 argument_default=None,
73 73 conflict_handler='error',
74 74 add_help=False):
75 75 if parents is None:
76 76 parents = []
77 77 super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
78 78 description=description, epilog=epilog,
79 79 parents=parents, formatter_class=formatter_class,
80 80 prefix_chars=prefix_chars, argument_default=argument_default,
81 81 conflict_handler=conflict_handler, add_help=add_help)
82 82
83 83 def error(self, message):
84 84 """ Raise a catchable error instead of exiting.
85 85 """
86 86 raise UsageError(message)
87 87
88 88 def parse_argstring(self, argstring):
89 89 """ Split a string into an argument list and parse that argument list.
90 90 """
91 91 argv = arg_split(argstring)
92 92 return self.parse_args(argv)
93 93
94 94
95 95 def construct_parser(magic_func):
96 96 """ Construct an argument parser using the function decorations.
97 97 """
98 98 kwds = getattr(magic_func, 'argcmd_kwds', {})
99 99 if 'description' not in kwds:
100 100 kwds['description'] = getattr(magic_func, '__doc__', None)
101 101 arg_name = real_name(magic_func)
102 102 parser = MagicArgumentParser(arg_name, **kwds)
103 103 # Reverse the list of decorators in order to apply them in the
104 104 # order in which they appear in the source.
105 105 group = None
106 106 for deco in magic_func.decorators[::-1]:
107 107 result = deco.add_to_parser(parser, group)
108 108 if result is not None:
109 109 group = result
110 110
111 111 # Replace the starting 'usage: ' with IPython's %.
112 112 help_text = parser.format_help()
113 113 if help_text.startswith('usage: '):
114 114 help_text = help_text.replace('usage: ', '%', 1)
115 115 else:
116 116 help_text = '%' + help_text
117 117
118 118 # Replace the magic function's docstring with the full help text.
119 119 magic_func.__doc__ = help_text
120 120
121 121 return parser
122 122
123 123
124 124 def parse_argstring(magic_func, argstring):
125 125 """ Parse the string of arguments for the given magic function.
126 126 """
127 127 return magic_func.parser.parse_argstring(argstring)
128 128
129 129
130 130 def real_name(magic_func):
131 131 """ Find the real name of the magic.
132 132 """
133 133 magic_name = magic_func.__name__
134 134 if magic_name.startswith('magic_'):
135 135 magic_name = magic_name[len('magic_'):]
136 136 return getattr(magic_func, 'argcmd_name', magic_name)
137 137
138 138
139 139 class ArgDecorator(object):
140 140 """ Base class for decorators to add ArgumentParser information to a method.
141 141 """
142 142
143 143 def __call__(self, func):
144 144 if not getattr(func, 'has_arguments', False):
145 145 func.has_arguments = True
146 146 func.decorators = []
147 147 func.decorators.append(self)
148 148 return func
149 149
150 150 def add_to_parser(self, parser, group):
151 151 """ Add this object's information to the parser, if necessary.
152 152 """
153 153 pass
154 154
155 155
156 156 class magic_arguments(ArgDecorator):
157 157 """ Mark the magic as having argparse arguments and possibly adjust the
158 158 name.
159 159 """
160 160
161 161 def __init__(self, name=None):
162 162 self.name = name
163 163
164 164 def __call__(self, func):
165 165 if not getattr(func, 'has_arguments', False):
166 166 func.has_arguments = True
167 167 func.decorators = []
168 168 if self.name is not None:
169 169 func.argcmd_name = self.name
170 170 # This should be the first decorator in the list of decorators, thus the
171 171 # last to execute. Build the parser.
172 172 func.parser = construct_parser(func)
173 173 return func
174 174
175 175
176 class argument(ArgDecorator):
177 """ Store arguments and keywords to pass to add_argument().
176 class ArgMethodWrapper(ArgDecorator):
177
178 """
179 Base class to define a wrapper for ArgumentParser method.
180
181 Child class must define either `_method_name` or `add_to_parser`.
178 182
179 Instances also serve to decorate command methods.
180 183 """
184
185 _method_name = None
186
181 187 def __init__(self, *args, **kwds):
182 188 self.args = args
183 189 self.kwds = kwds
184 190
185 191 def add_to_parser(self, parser, group):
186 192 """ Add this object's information to the parser.
187 193 """
188 194 if group is not None:
189 195 parser = group
190 parser.add_argument(*self.args, **self.kwds)
196 getattr(parser, self._method_name)(*self.args, **self.kwds)
191 197 return None
192 198
193 199
194 class argument_group(ArgDecorator):
200 class argument(ArgMethodWrapper):
201 """ Store arguments and keywords to pass to add_argument().
202
203 Instances also serve to decorate command methods.
204 """
205 _method_name = 'add_argument'
206
207
208 class defaults(ArgMethodWrapper):
209 """ Store arguments and keywords to pass to set_defaults().
210
211 Instances also serve to decorate command methods.
212 """
213 _method_name = 'set_defaults'
214
215
216 class argument_group(ArgMethodWrapper):
195 217 """ Store arguments and keywords to pass to add_argument_group().
196 218
197 219 Instances also serve to decorate command methods.
198 220 """
199 def __init__(self, *args, **kwds):
200 self.args = args
201 self.kwds = kwds
202 221
203 222 def add_to_parser(self, parser, group):
204 223 """ Add this object's information to the parser.
205 224 """
206 225 return parser.add_argument_group(*self.args, **self.kwds)
207 226
208 227
209 228 class kwds(ArgDecorator):
210 229 """ Provide other keywords to the sub-parser constructor.
211 230 """
212 231 def __init__(self, **kwds):
213 232 self.kwds = kwds
214 233
215 234 def __call__(self, func):
216 235 func = super(kwds, self).__call__(func)
217 236 func.argcmd_kwds = self.kwds
218 237 return func
219 238
220 239
221 240 __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
222 241 'parse_argstring']
@@ -1,289 +1,311 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 from IPython.external.argparse import Action
19 20
20 21 # Our own packages
21 22 from IPython.core.error import StdinNotImplementedError
22 23 from IPython.core.magic import Magics, magics_class, line_magic
24 from IPython.core.magic_arguments import (argument, magic_arguments,
25 parse_argstring)
23 26 from IPython.testing.skipdoctest import skip_doctest
24 27 from IPython.utils import io
25 28
26 29 #-----------------------------------------------------------------------------
27 30 # Magics class implementation
28 31 #-----------------------------------------------------------------------------
29 32
33
34 _unspecified = object()
35
36
30 37 @magics_class
31 38 class HistoryMagics(Magics):
32 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().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 @argument(
78 '-g', dest='pattern', nargs='*', default=None,
79 help="""
80 treat the arg as a glob pattern to search for in (full) history.
81 This includes the saved history (almost all commands ever written).
82 The pattern may contain '?' to match one unknown character and '*'
83 to match any number of unknown characters. Use '%%hist -g' to show
84 full saved history (may be very long).
85 """)
86 @argument(
87 '-l', dest='limit', type=int, nargs='?', default=_unspecified,
88 help="""
89 get the last n lines from all sessions. Specify n as a single
90 arg, or the default is the last 10 lines.
91 """)
92 @argument('range', nargs='*')
33 93 @skip_doctest
34 94 @line_magic
35 95 def history(self, parameter_s = ''):
36 96 """Print input history (_i<n> variables), with most recent last.
37 97
38 %history [-o -p -t -n] [-f filename] [range | -g pattern | -l number]
39
40 98 By default, input history is printed without line numbers so it can be
41 99 directly pasted into an editor. Use -n to show them.
42 100
43 101 By default, all input history from the current session is displayed.
44 102 Ranges of history can be indicated using the syntax:
45 103 4 : Line 4, current session
46 104 4-6 : Lines 4-6, current session
47 105 243/1-5: Lines 1-5, session 243
48 106 ~2/7 : Line 7, session 2 before current
49 107 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
50 108 of 6 sessions ago.
51 109 Multiple ranges can be entered, separated by spaces
52 110
53 111 The same syntax is used by %macro, %save, %edit, %rerun
54 112
55 Options:
56
57 -n: print line numbers for each input.
58 This feature is only available if numbered prompts are in use.
59
60 -o: also print outputs for each input.
61
62 -p: print classic '>>>' python prompts before each input. This is
63 useful for making documentation, and in conjunction with -o, for
64 producing doctest-ready output.
65
66 -r: (default) print the 'raw' history, i.e. the actual commands you
67 typed.
68
69 -t: print the 'translated' history, as IPython understands it.
70 IPython filters your input and converts it all into valid Python
71 source before executing it (things like magics or aliases are turned
72 into function calls, for example). With this option, you'll see the
73 native history instead of the user-entered version: '%cd /' will be
74 seen as 'get_ipython().magic("%cd /")' instead of '%cd /'.
75
76 -g: treat the arg as a pattern to grep for in (full) history.
77 This includes the saved history (almost all commands ever written).
78 The pattern may contain '?' to match one unknown character and '*'
79 to match any number of unknown characters. Use '%hist -g' to show
80 full saved history (may be very long).
81
82 -l: get the last n lines from all sessions. Specify n as a single
83 arg, or the default is the last 10 lines.
84
85 -f FILENAME: instead of printing the output to the screen, redirect
86 it to the given file. The file is always overwritten, though *when
87 it can*, IPython asks for confirmation first. In particular, running
88 the command 'history -f FILENAME' from the IPython Notebook
89 interface will replace FILENAME even if it already exists *without*
90 confirmation.
91
92 113 Examples
93 114 --------
94 115 ::
95 116
96 117 In [6]: %history -n 4-6
97 118 4:a = 12
98 119 5:print a**2
99 120 6:%history -n 4-6
100 121
101 122 """
102 123
103 124 if not self.shell.displayhook.do_full_cache:
104 125 print('This feature is only available if numbered prompts '
105 126 'are in use.')
106 127 return
107 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
128 args = parse_argstring(self.history, parameter_s)
108 129
109 130 # For brevity
110 131 history_manager = self.shell.history_manager
111 132
112 133 def _format_lineno(session, line):
113 134 """Helper function to format line numbers properly."""
114 135 if session in (0, history_manager.session_number):
115 136 return str(line)
116 137 return "%s/%s" % (session, line)
117 138
118 139 # Check if output to specific file was requested.
119 try:
120 outfname = opts['f']
121 except KeyError:
140 outfname = args.filename
141 if not outfname:
122 142 outfile = io.stdout # default
123 143 # We don't want to close stdout at the end!
124 144 close_at_end = False
125 145 else:
126 146 if os.path.exists(outfname):
127 147 try:
128 148 ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
129 149 except StdinNotImplementedError:
130 150 ans = True
131 151 if not ans:
132 152 print('Aborting.')
133 153 return
134 154 print("Overwriting file.")
135 155 outfile = io_open(outfname, 'w', encoding='utf-8')
136 156 close_at_end = True
137 157
138 print_nums = 'n' in opts
139 get_output = 'o' in opts
140 pyprompts = 'p' in opts
141 # Raw history is the default
142 raw = not('t' in opts)
158 print_nums = args.print_nums
159 get_output = args.get_output
160 pyprompts = args.pyprompts
161 raw = args.raw
143 162
144 163 pattern = None
164 limit = None if args.limit is _unspecified else args.limit
145 165
146 if 'g' in opts: # Glob search
147 pattern = "*" + args + "*" if args else "*"
148 hist = history_manager.search(pattern, raw=raw, output=get_output)
166 if args.pattern is not None:
167 if args.pattern:
168 pattern = "*" + " ".join(args.pattern) + "*"
169 else:
170 pattern = "*"
171 hist = history_manager.search(pattern, raw=raw, output=get_output,
172 n=limit)
149 173 print_nums = True
150 elif 'l' in opts: # Get 'tail'
151 try:
152 n = int(args)
153 except (ValueError, IndexError):
154 n = 10
174 elif args.limit is not _unspecified:
175 n = 10 if limit is None else limit
155 176 hist = history_manager.get_tail(n, raw=raw, output=get_output)
156 177 else:
157 if args: # Get history by ranges
158 hist = history_manager.get_range_by_str(args, raw, get_output)
178 if args.range: # Get history by ranges
179 hist = history_manager.get_range_by_str(" ".join(args.range),
180 raw, get_output)
159 181 else: # Just get history for the current session
160 182 hist = history_manager.get_range(raw=raw, output=get_output)
161 183
162 184 # We could be displaying the entire history, so let's not try to pull
163 185 # it into a list in memory. Anything that needs more space will just
164 186 # misalign.
165 187 width = 4
166 188
167 189 for session, lineno, inline in hist:
168 190 # Print user history with tabs expanded to 4 spaces. The GUI
169 191 # clients use hard tabs for easier usability in auto-indented code,
170 192 # but we want to produce PEP-8 compliant history for safe pasting
171 193 # into an editor.
172 194 if get_output:
173 195 inline, output = inline
174 196 inline = inline.expandtabs(4).rstrip()
175 197
176 198 multiline = "\n" in inline
177 199 line_sep = '\n' if multiline else ' '
178 200 if print_nums:
179 201 print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width),
180 202 line_sep), file=outfile, end=u'')
181 203 if pyprompts:
182 204 print(u">>> ", end=u"", file=outfile)
183 205 if multiline:
184 206 inline = "\n... ".join(inline.splitlines()) + "\n..."
185 207 print(inline, file=outfile)
186 208 if get_output and output:
187 209 print(output, file=outfile)
188 210
189 211 if close_at_end:
190 212 outfile.close()
191 213
192 214 @line_magic
193 215 def recall(self, arg):
194 216 r"""Repeat a command, or get command to input line for editing.
195 217
196 218 %recall and %rep are equivalent.
197 219
198 220 - %recall (no arguments):
199 221
200 222 Place a string version of last computation result (stored in the
201 223 special '_' variable) to the next input prompt. Allows you to create
202 224 elaborate command lines without using copy-paste::
203 225
204 226 In[1]: l = ["hei", "vaan"]
205 227 In[2]: "".join(l)
206 228 Out[2]: heivaan
207 229 In[3]: %recall
208 230 In[4]: heivaan_ <== cursor blinking
209 231
210 232 %recall 45
211 233
212 234 Place history line 45 on the next input prompt. Use %hist to find
213 235 out the number.
214 236
215 237 %recall 1-4
216 238
217 239 Combine the specified lines into one cell, and place it on the next
218 240 input prompt. See %history for the slice syntax.
219 241
220 242 %recall foo+bar
221 243
222 244 If foo+bar can be evaluated in the user namespace, the result is
223 245 placed at the next input prompt. Otherwise, the history is searched
224 246 for lines which contain that substring, and the most recent one is
225 247 placed at the next input prompt.
226 248 """
227 249 if not arg: # Last output
228 250 self.shell.set_next_input(str(self.shell.user_ns["_"]))
229 251 return
230 252 # Get history range
231 253 histlines = self.shell.history_manager.get_range_by_str(arg)
232 254 cmd = "\n".join(x[2] for x in histlines)
233 255 if cmd:
234 256 self.shell.set_next_input(cmd.rstrip())
235 257 return
236 258
237 259 try: # Variable in user namespace
238 260 cmd = str(eval(arg, self.shell.user_ns))
239 261 except Exception: # Search for term in history
240 262 histlines = self.shell.history_manager.search("*"+arg+"*")
241 263 for h in reversed([x[2] for x in histlines]):
242 264 if 'recall' in h or 'rep' in h:
243 265 continue
244 266 self.shell.set_next_input(h.rstrip())
245 267 return
246 268 else:
247 269 self.shell.set_next_input(cmd.rstrip())
248 270 print("Couldn't evaluate or find in history:", arg)
249 271
250 272 @line_magic
251 273 def rerun(self, parameter_s=''):
252 274 """Re-run previous input
253 275
254 276 By default, you can specify ranges of input history to be repeated
255 277 (as with %history). With no arguments, it will repeat the last line.
256 278
257 279 Options:
258 280
259 281 -l <n> : Repeat the last n lines of input, not including the
260 282 current command.
261 283
262 284 -g foo : Repeat the most recent line which contains foo
263 285 """
264 286 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
265 287 if "l" in opts: # Last n lines
266 288 n = int(opts['l'])
267 289 hist = self.shell.history_manager.get_tail(n)
268 290 elif "g" in opts: # Search
269 291 p = "*"+opts['g']+"*"
270 292 hist = list(self.shell.history_manager.search(p))
271 293 for l in reversed(hist):
272 294 if "rerun" not in l[2]:
273 295 hist = [l] # The last match which isn't a %rerun
274 296 break
275 297 else:
276 298 hist = [] # No matches except %rerun
277 299 elif args: # Specify history ranges
278 300 hist = self.shell.history_manager.get_range_by_str(args)
279 301 else: # Last line
280 302 hist = self.shell.history_manager.get_tail(1)
281 303 hist = [x[2] for x in hist]
282 304 if not hist:
283 305 print("No lines in history match specification")
284 306 return
285 307 histlines = "\n".join(hist)
286 308 print("=== Executing: ===")
287 309 print(histlines)
288 310 print("=== Output: ===")
289 311 self.shell.run_cell("\n".join(hist), store_history=False)
General Comments 0
You need to be logged in to leave comments. Login now