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