##// END OF EJS Templates
Simplify return form of some functions - avoid unnecessary variables.
Fernando Perez -
Show More
@@ -1,220 +1,217 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, 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
54 54
55 55 class MagicArgumentParser(argparse.ArgumentParser):
56 56 """ An ArgumentParser tweaked for use by IPython magics.
57 57 """
58 58 def __init__(self,
59 59 prog=None,
60 60 usage=None,
61 61 description=None,
62 62 epilog=None,
63 63 version=None,
64 64 parents=None,
65 65 formatter_class=argparse.HelpFormatter,
66 66 prefix_chars='-',
67 67 argument_default=None,
68 68 conflict_handler='error',
69 69 add_help=False):
70 70 if parents is None:
71 71 parents = []
72 72 super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
73 73 description=description, epilog=epilog, version=version,
74 74 parents=parents, formatter_class=formatter_class,
75 75 prefix_chars=prefix_chars, argument_default=argument_default,
76 76 conflict_handler=conflict_handler, add_help=add_help)
77 77
78 78 def error(self, message):
79 79 """ Raise a catchable error instead of exiting.
80 80 """
81 81 raise UsageError(message)
82 82
83 83 def parse_argstring(self, argstring):
84 84 """ Split a string into an argument list and parse that argument list.
85 85 """
86 86 argv = arg_split(argstring)
87 87 return self.parse_args(argv)
88 88
89 89
90 90 def construct_parser(magic_func):
91 91 """ Construct an argument parser using the function decorations.
92 92 """
93 93 kwds = getattr(magic_func, 'argcmd_kwds', {})
94 94 if 'description' not in kwds:
95 95 kwds['description'] = getattr(magic_func, '__doc__', None)
96 96 arg_name = real_name(magic_func)
97 97 parser = MagicArgumentParser(arg_name, **kwds)
98 98 # Reverse the list of decorators in order to apply them in the
99 99 # order in which they appear in the source.
100 100 group = None
101 101 for deco in magic_func.decorators[::-1]:
102 102 result = deco.add_to_parser(parser, group)
103 103 if result is not None:
104 104 group = result
105 105
106 106 # Replace the starting 'usage: ' with IPython's %.
107 107 help_text = parser.format_help()
108 108 if help_text.startswith('usage: '):
109 109 help_text = help_text.replace('usage: ', '%', 1)
110 110 else:
111 111 help_text = '%' + help_text
112 112
113 113 # Replace the magic function's docstring with the full help text.
114 114 magic_func.__doc__ = help_text
115 115
116 116 return parser
117 117
118 118
119 119 def parse_argstring(magic_func, argstring):
120 120 """ Parse the string of arguments for the given magic function.
121 121 """
122 args = magic_func.parser.parse_argstring(argstring)
123 return args
122 return magic_func.parser.parse_argstring(argstring)
124 123
125 124
126 125 def real_name(magic_func):
127 126 """ Find the real name of the magic.
128 127 """
129 128 magic_name = magic_func.__name__
130 129 if magic_name.startswith('magic_'):
131 130 magic_name = magic_name[len('magic_'):]
132 arg_name = getattr(magic_func, 'argcmd_name', magic_name)
133 return arg_name
131 return getattr(magic_func, 'argcmd_name', magic_name)
134 132
135 133
136 134 class ArgDecorator(object):
137 135 """ Base class for decorators to add ArgumentParser information to a method.
138 136 """
139 137
140 138 def __call__(self, func):
141 139 if not getattr(func, 'has_arguments', False):
142 140 func.has_arguments = True
143 141 func.decorators = []
144 142 func.decorators.append(self)
145 143 return func
146 144
147 145 def add_to_parser(self, parser, group):
148 146 """ Add this object's information to the parser, if necessary.
149 147 """
150 148 pass
151 149
152 150
153 151 class magic_arguments(ArgDecorator):
154 152 """ Mark the magic as having argparse arguments and possibly adjust the
155 153 name.
156 154 """
157 155
158 156 def __init__(self, name=None):
159 157 self.name = name
160 158
161 159 def __call__(self, func):
162 160 if not getattr(func, 'has_arguments', False):
163 161 func.has_arguments = True
164 162 func.decorators = []
165 163 if self.name is not None:
166 164 func.argcmd_name = self.name
167 165 # This should be the first decorator in the list of decorators, thus the
168 166 # last to execute. Build the parser.
169 167 func.parser = construct_parser(func)
170 168 return func
171 169
172 170
173 171 class argument(ArgDecorator):
174 172 """ Store arguments and keywords to pass to add_argument().
175 173
176 174 Instances also serve to decorate command methods.
177 175 """
178 176 def __init__(self, *args, **kwds):
179 177 self.args = args
180 178 self.kwds = kwds
181 179
182 180 def add_to_parser(self, parser, group):
183 181 """ Add this object's information to the parser.
184 182 """
185 183 if group is not None:
186 184 parser = group
187 185 parser.add_argument(*self.args, **self.kwds)
188 186 return None
189 187
190 188
191 189 class argument_group(ArgDecorator):
192 190 """ Store arguments and keywords to pass to add_argument_group().
193 191
194 192 Instances also serve to decorate command methods.
195 193 """
196 194 def __init__(self, *args, **kwds):
197 195 self.args = args
198 196 self.kwds = kwds
199 197
200 198 def add_to_parser(self, parser, group):
201 199 """ Add this object's information to the parser.
202 200 """
203 group = parser.add_argument_group(*self.args, **self.kwds)
204 return group
201 return parser.add_argument_group(*self.args, **self.kwds)
205 202
206 203
207 204 class kwds(ArgDecorator):
208 205 """ Provide other keywords to the sub-parser constructor.
209 206 """
210 207 def __init__(self, **kwds):
211 208 self.kwds = kwds
212 209
213 210 def __call__(self, func):
214 211 func = super(kwds, self).__call__(func)
215 212 func.argcmd_kwds = self.kwds
216 213 return func
217 214
218 215
219 216 __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
220 217 'parse_argstring']
General Comments 0
You need to be logged in to leave comments. Login now