##// END OF EJS Templates
Simplify return form of some functions - avoid unnecessary variables.
Fernando Perez -
Show More
@@ -1,220 +1,217 b''
1 ''' A decorator-based method of constructing IPython magics with `argparse`
1 ''' A decorator-based method of constructing IPython magics with `argparse`
2 option handling.
2 option handling.
3
3
4 New magic functions can be defined like so::
4 New magic functions can be defined like so::
5
5
6 from IPython.core.magic_arguments import (argument, magic_arguments,
6 from IPython.core.magic_arguments import (argument, magic_arguments,
7 parse_argstring)
7 parse_argstring)
8
8
9 @magic_arguments()
9 @magic_arguments()
10 @argument('-o', '--option', help='An optional argument.')
10 @argument('-o', '--option', help='An optional argument.')
11 @argument('arg', type=int, help='An integer positional argument.')
11 @argument('arg', type=int, help='An integer positional argument.')
12 def magic_cool(self, arg):
12 def magic_cool(self, arg):
13 """ A really cool magic command.
13 """ A really cool magic command.
14
14
15 """
15 """
16 args = parse_argstring(magic_cool, arg)
16 args = parse_argstring(magic_cool, arg)
17 ...
17 ...
18
18
19 The `@magic_arguments` decorator marks the function as having argparse arguments.
19 The `@magic_arguments` decorator marks the function as having argparse arguments.
20 The `@argument` decorator adds an argument using the same syntax as argparse's
20 The `@argument` decorator adds an argument using the same syntax as argparse's
21 `add_argument()` method. More sophisticated uses may also require the
21 `add_argument()` method. More sophisticated uses may also require the
22 `@argument_group` or `@kwds` decorator to customize the formatting and the
22 `@argument_group` or `@kwds` decorator to customize the formatting and the
23 parsing.
23 parsing.
24
24
25 Help text for the magic is automatically generated from the docstring and the
25 Help text for the magic is automatically generated from the docstring and the
26 arguments::
26 arguments::
27
27
28 In[1]: %cool?
28 In[1]: %cool?
29 %cool [-o OPTION] arg
29 %cool [-o OPTION] arg
30
30
31 A really cool magic command.
31 A really cool magic command.
32
32
33 positional arguments:
33 positional arguments:
34 arg An integer positional argument.
34 arg An integer positional argument.
35
35
36 optional arguments:
36 optional arguments:
37 -o OPTION, --option OPTION
37 -o OPTION, --option OPTION
38 An optional argument.
38 An optional argument.
39
39
40 '''
40 '''
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Copyright (c) 2010, IPython Development Team.
42 # Copyright (c) 2010, IPython Development Team.
43 #
43 #
44 # Distributed under the terms of the Modified BSD License.
44 # Distributed under the terms of the Modified BSD License.
45 #
45 #
46 # The full license is in the file COPYING.txt, distributed with this software.
46 # The full license is in the file COPYING.txt, distributed with this software.
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49 # Our own imports
49 # Our own imports
50 from IPython.external import argparse
50 from IPython.external import argparse
51 from IPython.core.error import UsageError
51 from IPython.core.error import UsageError
52 from IPython.utils.process import arg_split
52 from IPython.utils.process import arg_split
53
53
54
54
55 class MagicArgumentParser(argparse.ArgumentParser):
55 class MagicArgumentParser(argparse.ArgumentParser):
56 """ An ArgumentParser tweaked for use by IPython magics.
56 """ An ArgumentParser tweaked for use by IPython magics.
57 """
57 """
58 def __init__(self,
58 def __init__(self,
59 prog=None,
59 prog=None,
60 usage=None,
60 usage=None,
61 description=None,
61 description=None,
62 epilog=None,
62 epilog=None,
63 version=None,
63 version=None,
64 parents=None,
64 parents=None,
65 formatter_class=argparse.HelpFormatter,
65 formatter_class=argparse.HelpFormatter,
66 prefix_chars='-',
66 prefix_chars='-',
67 argument_default=None,
67 argument_default=None,
68 conflict_handler='error',
68 conflict_handler='error',
69 add_help=False):
69 add_help=False):
70 if parents is None:
70 if parents is None:
71 parents = []
71 parents = []
72 super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
72 super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
73 description=description, epilog=epilog, version=version,
73 description=description, epilog=epilog, version=version,
74 parents=parents, formatter_class=formatter_class,
74 parents=parents, formatter_class=formatter_class,
75 prefix_chars=prefix_chars, argument_default=argument_default,
75 prefix_chars=prefix_chars, argument_default=argument_default,
76 conflict_handler=conflict_handler, add_help=add_help)
76 conflict_handler=conflict_handler, add_help=add_help)
77
77
78 def error(self, message):
78 def error(self, message):
79 """ Raise a catchable error instead of exiting.
79 """ Raise a catchable error instead of exiting.
80 """
80 """
81 raise UsageError(message)
81 raise UsageError(message)
82
82
83 def parse_argstring(self, argstring):
83 def parse_argstring(self, argstring):
84 """ Split a string into an argument list and parse that argument list.
84 """ Split a string into an argument list and parse that argument list.
85 """
85 """
86 argv = arg_split(argstring)
86 argv = arg_split(argstring)
87 return self.parse_args(argv)
87 return self.parse_args(argv)
88
88
89
89
90 def construct_parser(magic_func):
90 def construct_parser(magic_func):
91 """ Construct an argument parser using the function decorations.
91 """ Construct an argument parser using the function decorations.
92 """
92 """
93 kwds = getattr(magic_func, 'argcmd_kwds', {})
93 kwds = getattr(magic_func, 'argcmd_kwds', {})
94 if 'description' not in kwds:
94 if 'description' not in kwds:
95 kwds['description'] = getattr(magic_func, '__doc__', None)
95 kwds['description'] = getattr(magic_func, '__doc__', None)
96 arg_name = real_name(magic_func)
96 arg_name = real_name(magic_func)
97 parser = MagicArgumentParser(arg_name, **kwds)
97 parser = MagicArgumentParser(arg_name, **kwds)
98 # Reverse the list of decorators in order to apply them in the
98 # Reverse the list of decorators in order to apply them in the
99 # order in which they appear in the source.
99 # order in which they appear in the source.
100 group = None
100 group = None
101 for deco in magic_func.decorators[::-1]:
101 for deco in magic_func.decorators[::-1]:
102 result = deco.add_to_parser(parser, group)
102 result = deco.add_to_parser(parser, group)
103 if result is not None:
103 if result is not None:
104 group = result
104 group = result
105
105
106 # Replace the starting 'usage: ' with IPython's %.
106 # Replace the starting 'usage: ' with IPython's %.
107 help_text = parser.format_help()
107 help_text = parser.format_help()
108 if help_text.startswith('usage: '):
108 if help_text.startswith('usage: '):
109 help_text = help_text.replace('usage: ', '%', 1)
109 help_text = help_text.replace('usage: ', '%', 1)
110 else:
110 else:
111 help_text = '%' + help_text
111 help_text = '%' + help_text
112
112
113 # Replace the magic function's docstring with the full help text.
113 # Replace the magic function's docstring with the full help text.
114 magic_func.__doc__ = help_text
114 magic_func.__doc__ = help_text
115
115
116 return parser
116 return parser
117
117
118
118
119 def parse_argstring(magic_func, argstring):
119 def parse_argstring(magic_func, argstring):
120 """ Parse the string of arguments for the given magic function.
120 """ Parse the string of arguments for the given magic function.
121 """
121 """
122 args = magic_func.parser.parse_argstring(argstring)
122 return magic_func.parser.parse_argstring(argstring)
123 return args
124
123
125
124
126 def real_name(magic_func):
125 def real_name(magic_func):
127 """ Find the real name of the magic.
126 """ Find the real name of the magic.
128 """
127 """
129 magic_name = magic_func.__name__
128 magic_name = magic_func.__name__
130 if magic_name.startswith('magic_'):
129 if magic_name.startswith('magic_'):
131 magic_name = magic_name[len('magic_'):]
130 magic_name = magic_name[len('magic_'):]
132 arg_name = getattr(magic_func, 'argcmd_name', magic_name)
131 return getattr(magic_func, 'argcmd_name', magic_name)
133 return arg_name
134
132
135
133
136 class ArgDecorator(object):
134 class ArgDecorator(object):
137 """ Base class for decorators to add ArgumentParser information to a method.
135 """ Base class for decorators to add ArgumentParser information to a method.
138 """
136 """
139
137
140 def __call__(self, func):
138 def __call__(self, func):
141 if not getattr(func, 'has_arguments', False):
139 if not getattr(func, 'has_arguments', False):
142 func.has_arguments = True
140 func.has_arguments = True
143 func.decorators = []
141 func.decorators = []
144 func.decorators.append(self)
142 func.decorators.append(self)
145 return func
143 return func
146
144
147 def add_to_parser(self, parser, group):
145 def add_to_parser(self, parser, group):
148 """ Add this object's information to the parser, if necessary.
146 """ Add this object's information to the parser, if necessary.
149 """
147 """
150 pass
148 pass
151
149
152
150
153 class magic_arguments(ArgDecorator):
151 class magic_arguments(ArgDecorator):
154 """ Mark the magic as having argparse arguments and possibly adjust the
152 """ Mark the magic as having argparse arguments and possibly adjust the
155 name.
153 name.
156 """
154 """
157
155
158 def __init__(self, name=None):
156 def __init__(self, name=None):
159 self.name = name
157 self.name = name
160
158
161 def __call__(self, func):
159 def __call__(self, func):
162 if not getattr(func, 'has_arguments', False):
160 if not getattr(func, 'has_arguments', False):
163 func.has_arguments = True
161 func.has_arguments = True
164 func.decorators = []
162 func.decorators = []
165 if self.name is not None:
163 if self.name is not None:
166 func.argcmd_name = self.name
164 func.argcmd_name = self.name
167 # This should be the first decorator in the list of decorators, thus the
165 # This should be the first decorator in the list of decorators, thus the
168 # last to execute. Build the parser.
166 # last to execute. Build the parser.
169 func.parser = construct_parser(func)
167 func.parser = construct_parser(func)
170 return func
168 return func
171
169
172
170
173 class argument(ArgDecorator):
171 class argument(ArgDecorator):
174 """ Store arguments and keywords to pass to add_argument().
172 """ Store arguments and keywords to pass to add_argument().
175
173
176 Instances also serve to decorate command methods.
174 Instances also serve to decorate command methods.
177 """
175 """
178 def __init__(self, *args, **kwds):
176 def __init__(self, *args, **kwds):
179 self.args = args
177 self.args = args
180 self.kwds = kwds
178 self.kwds = kwds
181
179
182 def add_to_parser(self, parser, group):
180 def add_to_parser(self, parser, group):
183 """ Add this object's information to the parser.
181 """ Add this object's information to the parser.
184 """
182 """
185 if group is not None:
183 if group is not None:
186 parser = group
184 parser = group
187 parser.add_argument(*self.args, **self.kwds)
185 parser.add_argument(*self.args, **self.kwds)
188 return None
186 return None
189
187
190
188
191 class argument_group(ArgDecorator):
189 class argument_group(ArgDecorator):
192 """ Store arguments and keywords to pass to add_argument_group().
190 """ Store arguments and keywords to pass to add_argument_group().
193
191
194 Instances also serve to decorate command methods.
192 Instances also serve to decorate command methods.
195 """
193 """
196 def __init__(self, *args, **kwds):
194 def __init__(self, *args, **kwds):
197 self.args = args
195 self.args = args
198 self.kwds = kwds
196 self.kwds = kwds
199
197
200 def add_to_parser(self, parser, group):
198 def add_to_parser(self, parser, group):
201 """ Add this object's information to the parser.
199 """ Add this object's information to the parser.
202 """
200 """
203 group = parser.add_argument_group(*self.args, **self.kwds)
201 return parser.add_argument_group(*self.args, **self.kwds)
204 return group
205
202
206
203
207 class kwds(ArgDecorator):
204 class kwds(ArgDecorator):
208 """ Provide other keywords to the sub-parser constructor.
205 """ Provide other keywords to the sub-parser constructor.
209 """
206 """
210 def __init__(self, **kwds):
207 def __init__(self, **kwds):
211 self.kwds = kwds
208 self.kwds = kwds
212
209
213 def __call__(self, func):
210 def __call__(self, func):
214 func = super(kwds, self).__call__(func)
211 func = super(kwds, self).__call__(func)
215 func.argcmd_kwds = self.kwds
212 func.argcmd_kwds = self.kwds
216 return func
213 return func
217
214
218
215
219 __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
216 __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
220 'parse_argstring']
217 'parse_argstring']
General Comments 0
You need to be logged in to leave comments. Login now