13588.1.5
by Aaron Bentley
Use inspection to determine default arguments. |
1 |
import inspect |
13588.1.7
by Aaron Bentley
Switch to command-from-function approach. |
2 |
from optparse import OptionParser |
13588.1.4
by Aaron Bentley
Extract common functionality to script_commands. |
3 |
|
4 |
||
5 |
class UserError(Exception): |
|
6 |
pass
|
|
7 |
||
8 |
||
13588.1.7
by Aaron Bentley
Switch to command-from-function approach. |
9 |
def add_dict(name, **kwargs): |
13588.1.12
by Aaron Bentley
Update docs. |
10 |
"""Decorator to add a named dictionary to a function's attributes.
|
11 |
||
12 |
The kwargs are the contents of the dict.
|
|
13 |
:param name: The name of the dictionary to add.
|
|
14 |
"""
|
|
13588.1.7
by Aaron Bentley
Switch to command-from-function approach. |
15 |
def decorator(func): |
16 |
setattr(func, name, kwargs) |
|
17 |
return func |
|
18 |
return decorator |
|
19 |
||
20 |
||
21 |
def types(**kwargs): |
|
13588.1.12
by Aaron Bentley
Update docs. |
22 |
"""Declare the types require by the arguments of a function.
|
23 |
||
24 |
The kwargs are the values to set, as used by OptionParser.add_option::
|
|
25 |
@types(port="int", delay=int)
|
|
26 |
"""
|
|
13588.1.7
by Aaron Bentley
Switch to command-from-function approach. |
27 |
return add_dict('_types', **kwargs) |
28 |
||
13588.1.9
by Aaron Bentley
Fix lint |
29 |
|
13588.1.7
by Aaron Bentley
Switch to command-from-function approach. |
30 |
def helps(**kwargs): |
13588.1.12
by Aaron Bentley
Update docs. |
31 |
"""Declare the help for the arguments of a function.
|
32 |
||
33 |
The kwargs are used to assign help::
|
|
34 |
helps(port="The port to use.", delay="The time to wait.")
|
|
35 |
"""
|
|
13588.1.7
by Aaron Bentley
Switch to command-from-function approach. |
36 |
return add_dict('_helps', **kwargs) |
37 |
||
38 |
||
39 |
def get_function_parser(function): |
|
13588.1.8
by Aaron Bentley
Remove Command. |
40 |
"""Generate an OptionParser for a function.
|
41 |
||
13588.1.12
by Aaron Bentley
Update docs. |
42 |
Option names are derived from the parameter names. Defaults come from the
|
43 |
parameter defaults. Types are inferred from the default, or may be
|
|
44 |
specified using the types decorator. Per-option help may be specified
|
|
45 |
using the helps decorator.
|
|
46 |
||
47 |
:param function: The function to generate a parser for.
|
|
13588.1.8
by Aaron Bentley
Remove Command. |
48 |
"""
|
13588.1.7
by Aaron Bentley
Switch to command-from-function approach. |
49 |
parser = OptionParser() |
50 |
args, ignore, ignored, defaults = inspect.getargspec(function) |
|
13588.1.13
by Aaron Bentley
Update from review. |
51 |
if defaults is None: |
52 |
defaults = [None] * len(args) |
|
13588.1.10
by Aaron Bentley
Infer option types from defaults if not specified. |
53 |
arg_types = getattr(function, '_types', {}) |
13588.1.16
by Aaron Bentley
Updates from review. |
54 |
arg_helps = getattr(function, '_helps', {}) |
13588.1.13
by Aaron Bentley
Update from review. |
55 |
for arg, default in zip(args, defaults): |
13588.1.10
by Aaron Bentley
Infer option types from defaults if not specified. |
56 |
arg_type = arg_types.get(arg) |
13588.1.7
by Aaron Bentley
Switch to command-from-function approach. |
57 |
if arg_type is None: |
13588.1.13
by Aaron Bentley
Update from review. |
58 |
if default is None: |
13588.1.10
by Aaron Bentley
Infer option types from defaults if not specified. |
59 |
continue
|
13588.1.13
by Aaron Bentley
Update from review. |
60 |
arg_type = type(default) |
13588.1.16
by Aaron Bentley
Updates from review. |
61 |
arg_help = arg_helps.get(arg) |
13588.1.7
by Aaron Bentley
Switch to command-from-function approach. |
62 |
if arg_help is not None: |
13588.1.9
by Aaron Bentley
Fix lint |
63 |
arg_help += ' Default: %default.' |
13588.1.13
by Aaron Bentley
Update from review. |
64 |
parser.add_option( |
65 |
'--%s' % arg, type=arg_type, help=arg_help, default=default) |
|
13588.1.7
by Aaron Bentley
Switch to command-from-function approach. |
66 |
return parser |
67 |
||
68 |
||
13588.1.8
by Aaron Bentley
Remove Command. |
69 |
def parse_args(command, args): |
13588.1.12
by Aaron Bentley
Update docs. |
70 |
"""Return the positional arguments as a dict.
|
71 |
||
72 |
:param command: A function to treat as a command.
|
|
73 |
:param args: The positional arguments supplied to that function.
|
|
74 |
:return: A dict mapping variable names to arguments.
|
|
75 |
"""
|
|
13588.1.8
by Aaron Bentley
Remove Command. |
76 |
# TODO: implement!
|
13588.1.15
by Aaron Bentley
Update docs. |
77 |
# Basically each argument without a default is treated as a positional
|
13588.1.12
by Aaron Bentley
Update docs. |
78 |
# argument. They must have types defined, since we can't infer it from
|
13588.1.14
by Aaron Bentley
Update docs. |
79 |
# the default.
|
13588.1.12
by Aaron Bentley
Update docs. |
80 |
#
|
81 |
# We may wish to declare optional positional arguments. These would have
|
|
82 |
# have defaults, but declaring them as positional would prevent them from
|
|
83 |
# being treated as flags.
|
|
13588.1.8
by Aaron Bentley
Remove Command. |
84 |
if len(args) != 0: |
85 |
raise UserError('Too many arguments.') |
|
86 |
return {} |
|
87 |
||
88 |
||
89 |
def run_from_args(command, cmd_args): |
|
13588.1.12
by Aaron Bentley
Update docs. |
90 |
"""Run a command function using the specified commandline arguments.
|
91 |
||
92 |
:param command: A function to treat as a command.
|
|
93 |
:param cmd_args: The commandline arguments to use to run the command.
|
|
94 |
"""
|
|
13588.1.8
by Aaron Bentley
Remove Command. |
95 |
parser = get_function_parser(command) |
96 |
options, args = parser.parse_args(cmd_args) |
|
97 |
kwargs = parse_args(command, args) |
|
98 |
kwargs.update(options.__dict__) |
|
99 |
command(**kwargs) |
|
100 |
||
101 |
||
102 |
def run_subcommand(subcommands, argv): |
|
13588.1.12
by Aaron Bentley
Update docs. |
103 |
"""Run a subcommand as specified by argv.
|
104 |
||
105 |
:param subcommands: A dict mapping subcommand names to functions.
|
|
106 |
:param argv: The arguments supplied by the user. argv[0] should be the
|
|
107 |
subcommand name.
|
|
108 |
"""
|
|
13588.1.8
by Aaron Bentley
Remove Command. |
109 |
if len(argv) < 1: |
110 |
raise UserError('Must supply a command: %s.' % |
|
111 |
', '.join(subcommands.keys())) |
|
112 |
try: |
|
113 |
command = subcommands[argv[0]] |
|
114 |
except KeyError: |
|
115 |
raise UserError('%s invalid. Valid commands: %s.' % |
|
116 |
(argv[0], ', '.join(subcommands.keys()))) |
|
117 |
run_from_args(command, argv[1:]) |