@@ -44,6 +44,7 @@ class Launcher:
44
44
45
45
def __init__ (self , description : str , long_text : str = "" , argv : Optional [List [str ]] = None ):
46
46
# pylint: disable=too-many-statements
47
+ # pylint: disable=too-many-locals
47
48
_LOG .info ("Launch: %s" , description )
48
49
epilog = """
49
50
Additional --key=value pairs can be specified to augment or override
@@ -56,7 +57,7 @@ def __init__(self, description: str, long_text: str = "", argv: Optional[List[st
56
57
<https://github.com/microsoft/MLOS/tree/main/mlos_bench/>
57
58
"""
58
59
parser = argparse .ArgumentParser (description = f"{ description } : { long_text } " , epilog = epilog )
59
- (args , args_rest ) = self ._parse_args (parser , argv )
60
+ (args , path_args , args_rest ) = self ._parse_args (parser , argv )
60
61
61
62
# Bootstrap config loader: command line takes priority.
62
63
config_path = args .config_path or []
@@ -87,11 +88,25 @@ def __init__(self, description: str, long_text: str = "", argv: Optional[List[st
87
88
88
89
self ._parent_service : Service = LocalExecService (parent = self ._config_loader )
89
90
91
+ # Prepare global_config from a combination of global config files, cli
92
+ # configs, and cli args.
93
+ args_dict = vars (args )
94
+ # teardown (bool) conflicts with Environment configs that use it for shell
95
+ # commands (list), so we exclude it from copying over
96
+ excluded_cli_args = path_args + ["teardown" ]
97
+ # Include (almost) any item from the cli config file that either isn't in
98
+ # the cli args at all or whose cli arg is missing.
99
+ cli_config_args = {
100
+ key : val
101
+ for (key , val ) in config .items ()
102
+ if (args_dict .get (key ) is None ) and key not in excluded_cli_args
103
+ }
104
+
90
105
self .global_config = self ._load_config (
91
- config .get ("globals" , []) + (args .globals or []),
92
- (args .config_path or []) + config .get ("config_path" , []),
93
- args_rest ,
94
- { key : val for ( key , val ) in config . items () if key not in vars ( args )} ,
106
+ args_globals = config .get ("globals" , []) + (args .globals or []),
107
+ config_path = (args .config_path or []) + config .get ("config_path" , []),
108
+ args_rest = args_rest ,
109
+ global_config = cli_config_args ,
95
110
)
96
111
# experiment_id is generally taken from --globals files, but we also allow
97
112
# overriding it on the CLI.
@@ -168,19 +183,35 @@ def service(self) -> Service:
168
183
def _parse_args (
169
184
parser : argparse .ArgumentParser ,
170
185
argv : Optional [List [str ]],
171
- ) -> Tuple [argparse .Namespace , List [str ]]:
186
+ ) -> Tuple [argparse .Namespace , List [str ], List [ str ] ]:
172
187
"""Parse the command line arguments."""
173
- parser .add_argument (
188
+
189
+ class PathArgsTracker :
190
+ """Simple class to help track which arguments are paths."""
191
+
192
+ def __init__ (self , parser : argparse .ArgumentParser ):
193
+ self ._parser = parser
194
+ self .path_args : List [str ] = []
195
+
196
+ def add_argument (self , * args : Any , ** kwargs : Any ) -> None :
197
+ """Add an argument to the parser and track its destination."""
198
+ self .path_args .append (self ._parser .add_argument (* args , ** kwargs ).dest )
199
+
200
+ path_args_tracker = PathArgsTracker (parser )
201
+
202
+ path_args_tracker .add_argument (
174
203
"--config" ,
175
204
required = False ,
176
- help = "Main JSON5 configuration file. Its keys are the same as the"
177
- + " command line options and can be overridden by the latter.\n "
178
- + "\n "
179
- + " See the `mlos_bench/config/` tree at https://github.com/microsoft/MLOS/ "
180
- + " for additional config examples for this and other arguments." ,
205
+ help = (
206
+ "Main JSON5 configuration file. Its keys are the same as the "
207
+ "command line options and can be overridden by the latter.\n "
208
+ "\n "
209
+ "See the `mlos_bench/config/` tree at https://github.com/microsoft/MLOS/ "
210
+ "for additional config examples for this and other arguments."
211
+ ),
181
212
)
182
213
183
- parser .add_argument (
214
+ path_args_tracker .add_argument (
184
215
"--log_file" ,
185
216
"--log-file" ,
186
217
required = False ,
@@ -192,11 +223,13 @@ def _parse_args(
192
223
"--log-level" ,
193
224
required = False ,
194
225
type = str ,
195
- help = f"Logging level. Default is { logging .getLevelName (_LOG_LEVEL )} ."
196
- + " Set to DEBUG for debug, WARNING for warnings only." ,
226
+ help = (
227
+ f"Logging level. Default is { logging .getLevelName (_LOG_LEVEL )} . "
228
+ "Set to DEBUG for debug, WARNING for warnings only."
229
+ ),
197
230
)
198
231
199
- parser .add_argument (
232
+ path_args_tracker .add_argument (
200
233
"--config_path" ,
201
234
"--config-path" ,
202
235
"--config-paths" ,
@@ -207,7 +240,7 @@ def _parse_args(
207
240
help = "One or more locations of JSON config files." ,
208
241
)
209
242
210
- parser .add_argument (
243
+ path_args_tracker .add_argument (
211
244
"--service" ,
212
245
"--services" ,
213
246
nargs = "+" ,
@@ -219,17 +252,19 @@ def _parse_args(
219
252
),
220
253
)
221
254
222
- parser .add_argument (
255
+ path_args_tracker .add_argument (
223
256
"--environment" ,
224
257
required = False ,
225
258
help = "Path to JSON file with the configuration of the benchmarking environment(s)." ,
226
259
)
227
260
228
- parser .add_argument (
261
+ path_args_tracker .add_argument (
229
262
"--optimizer" ,
230
263
required = False ,
231
- help = "Path to the optimizer configuration file. If omitted, run"
232
- + " a single trial with default (or specified in --tunable_values)." ,
264
+ help = (
265
+ "Path to the optimizer configuration file. If omitted, run "
266
+ "a single trial with default (or specified in --tunable_values)."
267
+ ),
233
268
)
234
269
235
270
parser .add_argument (
@@ -243,18 +278,22 @@ def _parse_args(
243
278
),
244
279
)
245
280
246
- parser .add_argument (
281
+ path_args_tracker .add_argument (
247
282
"--scheduler" ,
248
283
required = False ,
249
- help = "Path to the scheduler configuration file. By default, use"
250
- + " a single worker synchronous scheduler." ,
284
+ help = (
285
+ "Path to the scheduler configuration file. By default, use "
286
+ "a single worker synchronous scheduler."
287
+ ),
251
288
)
252
289
253
- parser .add_argument (
290
+ path_args_tracker .add_argument (
254
291
"--storage" ,
255
292
required = False ,
256
- help = "Path to the storage configuration file."
257
- + " If omitted, use the ephemeral in-memory SQL storage." ,
293
+ help = (
294
+ "Path to the storage configuration file. "
295
+ "If omitted, use the ephemeral in-memory SQL storage."
296
+ ),
258
297
)
259
298
260
299
parser .add_argument (
@@ -275,24 +314,28 @@ def _parse_args(
275
314
help = "Seed to use with --random_init" ,
276
315
)
277
316
278
- parser .add_argument (
317
+ path_args_tracker .add_argument (
279
318
"--tunable_values" ,
280
319
"--tunable-values" ,
281
320
nargs = "+" ,
282
321
action = "extend" ,
283
322
required = False ,
284
- help = "Path to one or more JSON files that contain values of the tunable"
285
- + " parameters. This can be used for a single trial (when no --optimizer"
286
- + " is specified) or as default values for the first run in optimization." ,
323
+ help = (
324
+ "Path to one or more JSON files that contain values of the tunable "
325
+ "parameters. This can be used for a single trial (when no --optimizer "
326
+ "is specified) or as default values for the first run in optimization."
327
+ ),
287
328
)
288
329
289
- parser .add_argument (
330
+ path_args_tracker .add_argument (
290
331
"--globals" ,
291
332
nargs = "+" ,
292
333
action = "extend" ,
293
334
required = False ,
294
- help = "Path to one or more JSON files that contain additional"
295
- + " [private] parameters of the benchmarking environment." ,
335
+ help = (
336
+ "Path to one or more JSON files that contain additional "
337
+ "[private] parameters of the benchmarking environment."
338
+ ),
296
339
)
297
340
298
341
parser .add_argument (
@@ -328,7 +371,7 @@ def _parse_args(
328
371
argv = sys .argv [1 :].copy ()
329
372
(args , args_rest ) = parser .parse_known_args (argv )
330
373
331
- return (args , args_rest )
374
+ return (args , path_args_tracker . path_args , args_rest )
332
375
333
376
@staticmethod
334
377
def _try_parse_extra_args (cmdline : Iterable [str ]) -> Dict [str , TunableValue ]:
@@ -361,6 +404,7 @@ def _try_parse_extra_args(cmdline: Iterable[str]) -> Dict[str, TunableValue]:
361
404
362
405
def _load_config (
363
406
self ,
407
+ * ,
364
408
args_globals : Iterable [str ],
365
409
config_path : Iterable [str ],
366
410
args_rest : Iterable [str ],
0 commit comments