Coverage for /home/ubuntu/Documents/Research/mut_p6/sacred/sacred/arg_parser.py: 25%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Contains the command-line parsing and help for experiments.
4The command-line interface of sacred is built on top of ``docopt``, which
5constructs a command-line parser from a usage text. Curiously in sacred we
6first programmatically generate a usage text and then parse it with ``docopt``.
7"""
9import ast
10import textwrap
11import inspect
12from shlex import quote
14from sacred.serializer import restore
15from sacred.settings import SETTINGS
16from sacred.utils import set_by_dotted_path
17from sacred.commandline_options import CLIOption
20__all__ = ("get_config_updates", "format_usage")
23USAGE_TEMPLATE = """Usage:
24 {program_name} [(with UPDATE...)] [options]
25 {program_name} help [COMMAND]
26 {program_name} (-h | --help)
27 {program_name} COMMAND [(with UPDATE...)] [options]
29{description}
31Options:
32{options}
34Arguments:
35 COMMAND Name of command to run (see below for list of commands)
36 UPDATE Configuration assignments of the form foo.bar=17
37{arguments}
38{commands}"""
41def get_config_updates(updates):
42 """
43 Parse the UPDATES given on the commandline.
45 Parameters
46 ----------
47 updates (list[str]):
48 list of update-strings of the form NAME=LITERAL or just NAME.
50 Returns
51 -------
52 (dict, list):
53 Config updates and named configs to use
55 """
56 config_updates = {}
57 named_configs = []
58 if not updates:
59 return config_updates, named_configs
60 for upd in updates:
61 if upd == "":
62 continue
63 path, sep, value = upd.partition("=")
64 if sep == "=":
65 path = path.strip() # get rid of surrounding whitespace
66 value = value.strip() # get rid of surrounding whitespace
67 set_by_dotted_path(config_updates, path, _convert_value(value))
68 else:
69 named_configs.append(path)
70 return config_updates, named_configs
73def _format_options_usage(options):
74 """
75 Format the Options-part of the usage text.
77 Parameters
78 ----------
79 options : list[sacred.commandline_options.CommandLineOption]
80 A list of all supported commandline options.
82 Returns
83 -------
84 str
85 Text formatted as a description for the commandline options
87 """
88 options_usage = ""
89 for op in options:
90 short, long = op.get_flags()
91 if op.arg:
92 flag = "{short} {arg} {long}={arg}".format(
93 short=short, long=long, arg=op.arg
94 )
95 else:
96 flag = "{short} {long}".format(short=short, long=long)
98 if isinstance(op, CLIOption):
99 doc = op.get_description()
100 else:
101 # legacy
102 doc = inspect.cleandoc(op.__doc__)
103 wrapped_description = textwrap.wrap(
104 doc, width=79, initial_indent=" " * 32, subsequent_indent=" " * 32
105 )
106 wrapped_description = "\n".join(wrapped_description).strip()
108 options_usage += " {:28} {}\n".format(flag, wrapped_description)
109 return options_usage
112def _format_arguments_usage(options):
113 """
114 Construct the Arguments-part of the usage text.
116 Parameters
117 ----------
118 options : list[sacred.commandline_options.CommandLineOption]
119 A list of all supported commandline options.
121 Returns
122 -------
123 str
124 Text formatted as a description of the arguments supported by the
125 commandline options.
127 """
128 argument_usage = ""
129 for op in options:
130 if op.arg and op.arg_description:
131 wrapped_description = textwrap.wrap(
132 op.arg_description,
133 width=79,
134 initial_indent=" " * 12,
135 subsequent_indent=" " * 12,
136 )
137 wrapped_description = "\n".join(wrapped_description).strip()
138 argument_usage += " {:8} {}\n".format(op.arg, wrapped_description)
139 return argument_usage
142def _format_command_usage(commands):
143 """
144 Construct the Commands-part of the usage text.
146 Parameters
147 ----------
148 commands : dict[str, func]
149 dictionary of supported commands.
150 Each entry should be a tuple of (name, function).
152 Returns
153 -------
154 str
155 Text formatted as a description of the commands.
157 """
158 if not commands:
159 return ""
160 command_usage = "\nCommands:\n"
161 cmd_len = max([len(c) for c in commands] + [8])
163 for cmd_name, cmd_doc in commands.items():
164 cmd_doc = _get_first_line_of_docstring(cmd_doc)
165 command_usage += (" {:%d} {}\n" % cmd_len).format(cmd_name, cmd_doc)
166 return command_usage
169def format_usage(program_name, description, commands=None, options=()):
170 """
171 Construct the usage text.
173 Parameters
174 ----------
175 program_name : str
176 Usually the name of the python file that contains the experiment.
177 description : str
178 description of this experiment (usually the docstring).
179 commands : dict[str, func]
180 Dictionary of supported commands.
181 Each entry should be a tuple of (name, function).
182 options : list[sacred.commandline_options.CommandLineOption]
183 A list of all supported commandline options.
185 Returns
186 -------
187 str
188 The complete formatted usage text for this experiment.
189 It adheres to the structure required by ``docopt``.
191 """
192 usage = USAGE_TEMPLATE.format(
193 program_name=quote(program_name),
194 description=description.strip() if description else "",
195 options=_format_options_usage(options),
196 arguments=_format_arguments_usage(options),
197 commands=_format_command_usage(commands),
198 )
199 return usage
202def _get_first_line_of_docstring(func):
203 return textwrap.dedent(func.__doc__ or "").strip().split("\n")[0]
206def _convert_value(value):
207 """Parse string as python literal if possible and fallback to string."""
208 try:
209 return restore(ast.literal_eval(value))
210 except (ValueError, SyntaxError):
211 if SETTINGS.COMMAND_LINE.STRICT_PARSING:
212 raise
213 # use as string if nothing else worked
214 return value