Coverage for sparkle/CLI/help/argparse_custom.py: 93%
104 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-01 13:21 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-01 13:21 +0000
1"""Custom helper class and functions to process CLI arguments with argparse."""
3from __future__ import annotations
4import argparse
5import enum
6from pathlib import Path
7from typing import Any
9from runrunner.base import Runner
11from sparkle.platform.settings_objects import SettingState, Settings
14class SetByUser(argparse.Action):
15 """Possible action to execute for CLI argument."""
17 def __call__(self: SetByUser, parser: argparse.ArgumentParser,
18 namespace: argparse.Namespace, values: str, option_string: str = None)\
19 -> None:
20 """Set attributes when called."""
21 setattr(namespace, self.dest, values)
22 setattr(namespace, self.dest + "_nondefault", True)
25# Taken from https://stackoverflow.com/a/60750535
26class EnumAction(argparse.Action):
27 """Argparse action for handling Enums."""
29 def __init__(self: EnumAction, **kwargs: str) -> None:
30 """Initialise the EnumAction."""
31 # Pop off the type value
32 enum_type = kwargs.pop("type", None)
34 # Ensure an Enum subclass is provided
35 if enum_type is None:
36 raise ValueError("type must be assigned an Enum when using EnumAction")
37 if not issubclass(enum_type, enum.Enum):
38 raise TypeError("type must be an Enum when using EnumAction")
40 # Generate choices from the Enum
41 kwargs.setdefault("choices", tuple(e.value for e in enum_type))
43 super(EnumAction, self).__init__(**kwargs)
45 self._enum = enum_type
47 def __call__(self: EnumAction, parser: argparse.ArgumentParser,
48 namespace: argparse.Namespace, values: str, option_string: str = None) \
49 -> None:
50 """Converts value back to Enum."""
51 value = self._enum(values)
52 setattr(namespace, self.dest, value)
55def user_set_state(args: argparse.Namespace, arg_name: str) -> SettingState:
56 """Return the SettingState of an argument."""
57 if hasattr(args, arg_name + "_nondefault"):
58 return SettingState.CMD_LINE
59 else:
60 return SettingState.DEFAULT
63class ArgumentContainer():
64 """Helper class for more convenient argument packaging and access."""
66 def __init__(self: ArgumentContainer, names: list[str], kwargs: dict[str, Any])\
67 -> None:
68 """Create an ArgumentContainer.
70 Args:
71 names: List of names for the contained argument. For positional arguments
72 this will typically contain two, the first one starting with '-' and the
73 second one starting with '--'.
74 kwargs: Keyword arguments needed by the method parser.add_argument which adds
75 the contained argument to a parser.
76 """
77 self.names = names
78 self.kwargs = kwargs
81ActualMarginalContributionArgument = \
82 ArgumentContainer(names=["--actual"],
83 kwargs={"action": "store_true",
84 "help": "compute the marginal contribution "
85 "for the actual selector"})
87AllJobsArgument = \
88 ArgumentContainer(names=["--all"],
89 kwargs={"action": "store_true",
90 "help": "use all known job ID(s) for the command"})
92AllSolverConfigurationArgument = \
93 ArgumentContainer(names=["--all-configurations"],
94 kwargs={"action": "store_true",
95 "help": "use all known configurations for the command"})
97AllConfigurationArgument = \
98 ArgumentContainer(names=["--all-configurations"],
99 kwargs={"action": "store_true",
100 "help": "use all known Solver configurations"})
102BestConfigurationArgument = \
103 ArgumentContainer(names=["--best-configuration"],
104 kwargs={"required": False,
105 "nargs": "?",
106 "type": Path,
107 "const": True,
108 "default": False,
109 "help": "Paths to instance(s) or instanceset(s) over "
110 "which to determine the best configuration. If "
111 "empty, all known instances are used."})
113BestSolverConfigurationArgument = \
114 ArgumentContainer(names=["--best-configuration"],
115 kwargs={"action": "store_true",
116 "help": "use the best configurations for the command"})
118CancelJobsArgument = \
119 ArgumentContainer(names=["--cancel"],
120 kwargs={"action": "store_true",
121 "help": "cancel the job(s) with the given ID(s)"})
123CheckTypeArgument = \
124 ArgumentContainer(names=["type"],
125 kwargs={"choices": ["extractor",
126 "feature-extractor",
127 "solver",
128 "instance-set",
129 "Extractor"
130 "Feature-Extractor",
131 "Instance-Set",
132 "Solver",
133 "FeatureExtractor",
134 "InstanceSet"],
135 "help": "type of the object to check"})
137CheckPathArgument = \
138 ArgumentContainer(names=["path"],
139 kwargs={"type": Path,
140 "help": "path to the object to check"})
142CleanupArgumentAll = \
143 ArgumentContainer(names=["--all"],
144 kwargs={"action": "store_true",
145 "help": "clean all output files"})
147CleanupArgumentRemove = \
148 ArgumentContainer(names=["--remove"],
149 kwargs={"action": "store_true",
150 "help": "remove all files in the platform, including "
151 "user data such as InstanceSets and Solvers"})
153CleanUpPerformanceDataArgument = \
154 ArgumentContainer(names=["--performance-data"],
155 kwargs={"action": "store_true",
156 "help": "clean performance data from empty lines"})
158ConfigurationArgument = \
159 ArgumentContainer(names=["--configuration"],
160 kwargs={"required": False,
161 "type": str,
162 "nargs": "+",
163 "help": "The indices of which configurations to use"})
165ConfiguratorArgument = ArgumentContainer(names=["--configurator"],
166 kwargs={"type": str,
167 "help": "name of the configurator"})
169CPUTimeArgument = \
170 ArgumentContainer(names=["--cpu-time"],
171 kwargs={"type": int,
172 "help": "configuration budget per configurator run in "
173 "seconds (cpu)"})
175CutOffTimeArgument = \
176 ArgumentContainer(names=["--cutoff-time", "--cutoff", "--timeout"],
177 kwargs={"type": int,
178 "help": "The duration the portfolio will run before the "
179 "solvers within the portfolio will be stopped "
180 "(default: "
181 f"{Settings.DEFAULT_general_solver_cutoff_time})"})
183DefaultSolverConfigurationArgument = \
184 ArgumentContainer(names=["--default-configuration"],
185 kwargs={"action": "store_true",
186 "help": "use the default configurations for the command"})
188DeterministicArgument =\
189 ArgumentContainer(names=["--deterministic"],
190 kwargs={"action": "store_true",
191 "help": "Flag indicating the solver is deterministic"})
193DownloadExamplesArgument =\
194 ArgumentContainer(names=["--download-examples"],
195 kwargs={"action": "store_true",
196 "default": False,
197 "required": False,
198 "help": "Download the Examples into the directory."})
200ExtractorPathArgument = ArgumentContainer(names=["extractor_path"],
201 kwargs={"metavar": "extractor-path",
202 "type": str,
203 "help": "path or nickname of the "
204 "feature extractor"
205 })
207GenerateJSONArgument = ArgumentContainer(names=["--only-json"],
208 kwargs={"required": False,
209 "default": False,
210 "type": bool,
211 "help": "if set to True, only generate "
212 "machine readable output"
213 })
215InstancePathOptional =\
216 ArgumentContainer(names=["instance_path"],
217 kwargs={"type": Path,
218 "nargs": "?",
219 "help": "Path to an instance to use for the command"})
221InstanceSetPathsArgument =\
222 ArgumentContainer(names=["--instance-path", "--instance-set-path",
223 "--instance", "--instance-set",
224 "--instances", "--instance-sets",
225 "--instance-paths", "--instance-set-paths"],
226 kwargs={"required": False,
227 "nargs": "+",
228 "type": Path,
229 "help": "Path to an instance (set)"})
231InstanceSetRequiredArgument = \
232 ArgumentContainer(names=["--instance", "--instance-set"],
233 kwargs={"required": True,
234 "type": Path,
235 "help": "path to instance (set)"})
238InstanceSetTestArgument = \
239 ArgumentContainer(names=["--instance-set-test"],
240 kwargs={"required": False,
241 "type": Path,
242 "help": "path to test instance set"})
244InstanceSetTrainArgument = \
245 ArgumentContainer(names=["--instance-set-train"],
246 kwargs={"required": True,
247 "type": Path,
248 "help": "path to training instance set"})
250InstanceSetTestAblationArgument = \
251 ArgumentContainer(names=["--instance-set-test"],
252 kwargs={"required": False,
253 "type": str,
254 "help": "path to test instance set"})
256InstanceSetTrainOptionalArgument = \
257 ArgumentContainer(names=["--instance-set-train"],
258 kwargs={"required": False,
259 "type": Path,
260 "help": "path to training instance set"})
262InstanceSetsReportArgument = \
263 ArgumentContainer(names=["--instance-sets", "--instance-set"],
264 kwargs={"required": False,
265 "nargs": "+",
266 "type": str,
267 "help": "Instance Set(s) to use for the report. If not "
268 "specified, all Instance Sets are used."})
270InstancesPathArgument = ArgumentContainer(names=["instances_path"],
271 kwargs={"metavar": "instances-path",
272 "type": str,
273 "help": "path to the instance set"})
275InstancesPathRemoveArgument = \
276 ArgumentContainer(names=["instances_path"],
277 kwargs={"metavar": "instances-path",
278 "type": str,
279 "help": "path to or nickname of the instance set"})
281JobIDsArgument = ArgumentContainer(names=["--job-ids"],
282 kwargs={"required": False,
283 "nargs": "+",
284 "type": str,
285 "default": None,
286 "help": "job ID(s) to use for the command"})
288NicknameFeatureExtractorArgument = \
289 ArgumentContainer(names=["--nickname"],
290 kwargs={"type": str,
291 "help": "set a nickname for the feature extractor"})
293NicknameInstanceSetArgument = \
294 ArgumentContainer(names=["--nickname"],
295 kwargs={"type": str,
296 "help": "set a nickname for the instance set"})
298NicknamePortfolioArgument = \
299 ArgumentContainer(names=["--portfolio-name"],
300 kwargs={"type": Path,
301 "help": "Specify a name of the portfolio. "
302 "If none is given, one will be generated."})
304NicknameSolverArgument = \
305 ArgumentContainer(names=["--nickname"],
306 kwargs={"type": str,
307 "help": "set a nickname for the solver"})
309NoCopyArgument = ArgumentContainer(names=["--no-copy"],
310 kwargs={"action": "store_true",
311 "required": False,
312 "help": "do not copy the source directory to "
313 "the platform directory, but create a"
314 " symbolic link instead"})
316NoSavePlatformArgument = ArgumentContainer(names=["--no-save"],
317 kwargs={"action": "store_false",
318 "default": True,
319 "required": False,
320 "help": "do not save the platform "
321 "upon re-initialisation."})
323NumberOfRunsConfigurationArgument = \
324 ArgumentContainer(names=["--number-of-runs"],
325 kwargs={"type": int,
326 "help": "number of configuration runs to execute"})
328NumberOfRunsAblationArgument = \
329 ArgumentContainer(names=["--number-of-runs"],
330 kwargs={"type": int,
331 "help": "Number of configuration runs to execute"})
333PerfectSelectorMarginalContributionArgument =\
334 ArgumentContainer(names=["--perfect"],
335 kwargs={"action": "store_true",
336 "help": "compute the marginal contribution "
337 "for the perfect selector"})
339RacingArgument = ArgumentContainer(names=["--racing"],
340 kwargs={"type": bool,
341 "help": "Performs abaltion analysis with "
342 "racing"})
344RecomputeFeaturesArgument = \
345 ArgumentContainer(names=["--recompute"],
346 kwargs={"action": "store_true",
347 "help": "Re-run feature extractor for instances with "
348 "previously computed features"})
350RecomputeMarginalContributionArgument = \
351 ArgumentContainer(names=["--recompute"],
352 kwargs={"action": "store_true",
353 "help": "force marginal contribution to be recomputed even"
354 " when it already exists in file for the current "
355 "selector"})
357RecomputeMarginalContributionForSelectorArgument = \
358 ArgumentContainer(names=["--recompute-marginal-contribution"],
359 kwargs={"action": "store_true",
360 "help": "force marginal contribution to be recomputed even"
361 " when it already exists in file for the current "
362 "selector"})
364RecomputePortfolioSelectorArgument = \
365 ArgumentContainer(names=["--recompute-portfolio-selector"],
366 kwargs={"action": "store_true",
367 "help": "force the construction of a new portfolio "
368 "selector even when it already exists for the current "
369 "feature and performance data. NOTE: This will also "
370 "result in the computation of the marginal contributions "
371 "of solvers to the new portfolio selector."})
373RecomputeRunSolversArgument = \
374 ArgumentContainer(names=["--recompute"],
375 kwargs={"action": "store_true",
376 "help": "recompute the performance of all solvers on all "
377 "instances"})
379PerformanceDataJobsArgument = \
380 ArgumentContainer(names=["--performance-data-jobs"],
381 kwargs={"action": "store_true",
382 "help": "compute the remaining jobs in the Performance "
383 "DataFrame"})
385RebuildRunsolverArgument = \
386 ArgumentContainer(names=["--rebuild-runsolver"],
387 kwargs={"action": "store_true",
388 "required": False,
389 "default": False,
390 "help": "Clean the RunSolver executable and rebuild it."})
392RunOnArgument = ArgumentContainer(names=["--run-on"],
393 kwargs={"type": Runner,
394 "choices": [Runner.LOCAL,
395 Runner.SLURM],
396 "action": EnumAction,
397 "help": "On which computer or cluster "
398 "environment to execute the "
399 "calculation."})
401SeedArgument = ArgumentContainer(names=["--seed"],
402 kwargs={"type": int,
403 "help": "seed to use for the command"})
405SelectionScenarioArgument = \
406 ArgumentContainer(names=["--selection-scenario"],
407 kwargs={"required": True,
408 "type": Path,
409 "help": "the selection scenario to use"})
411SelectorAblationArgument =\
412 ArgumentContainer(names=["--solver-ablation"],
413 kwargs={"required": False,
414 "action": "store_true",
415 "help": "construct a selector for "
416 "each solver ablation combination"})
418SettingsFileArgument = \
419 ArgumentContainer(names=["--settings-file"],
420 kwargs={"type": Path,
421 "help": "Specify the settings file to use in case you want"
422 " to use one other than the default"})
424SkipChecksArgument = ArgumentContainer(
425 names=["--skip-checks"],
426 kwargs={"dest": "run_checks",
427 "default": True,
428 "action": "store_false",
429 "help": "Checks the solver's functionality by testing it on an instance "
430 "and the pcs file, when applicable."})
432SnapshotArgument = ArgumentContainer(names=["snapshot_file_path"],
433 kwargs={"metavar": "snapshot-file-path",
434 "type": str,
435 "help": "path to the snapshot file"})
437SnapshotNameArgument = ArgumentContainer(names=["--name"],
438 kwargs={"required": False,
439 "type": str,
440 "help": "name of the snapshot"})
442SolverArgument = ArgumentContainer(names=["--solver"],
443 kwargs={"required": True,
444 "type": Path,
445 "help": "path to solver"})
447SolversArgument = ArgumentContainer(names=["--solvers", "--solver-paths",
448 "--solver", "--solver-path"],
449 kwargs={"required": False,
450 "nargs": "+",
451 "type": str,
452 "help": "Specify the list of solvers to be "
453 "used. If not specifed, all solvers "
454 "known in Sparkle will be used."})
456SolverCallsArgument = \
457 ArgumentContainer(names=["--solver-calls"],
458 kwargs={"type": int,
459 "help": "number of solver calls to execute"})
461SolverSeedsArgument = \
462 ArgumentContainer(names=["--solver-seeds"],
463 kwargs={"type": int,
464 "help": "number of random seeds per solver to execute"})
466SolverRemoveArgument = \
467 ArgumentContainer(names=["solver"],
468 kwargs={"metavar": "solver",
469 "type": str,
470 "help": "name, path to or nickname of the solver"})
472SolverPathArgument = ArgumentContainer(names=["solver_path"],
473 kwargs={"metavar": "solver-path",
474 "type": str,
475 "help": "path to the solver"})
477SolversReportArgument = ArgumentContainer(
478 names=["--solvers", "--solver"],
479 kwargs={"nargs": "+",
480 "type": str,
481 "default": None,
482 "help": "Solver(s) to use for the report. If not specified, all solvers are "
483 "included."})
485SolverCutOffTimeArgument = \
486 ArgumentContainer(names=["--solver-cutoff-time", "--target-cutoff-time"],
487 kwargs={"type": int,
488 "help": "cutoff time per Solver run in seconds"})
490TestSetRunAllConfigurationArgument = \
491 ArgumentContainer(names=["--test-set-run-all-configurations"],
492 kwargs={"required": False,
493 "action": "store_true",
494 "help": "run all found configurations on the test set"})
496UseFeaturesArgument = ArgumentContainer(names=["--use-features"],
497 kwargs={"required": False,
498 "action": "store_true",
499 "help": "use the training set's features"
500 " for configuration"})
502VerboseArgument = ArgumentContainer(names=["--verbose", "-v"],
503 kwargs={"action": "store_true",
504 "help": "output status in verbose mode"})
506WallClockTimeArgument = \
507 ArgumentContainer(names=["--wallclock-time"],
508 kwargs={"type": int,
509 "help": "configuration budget per configurator run in "
510 "seconds (wallclock)"})
512SolutionVerifierArgument = \
513 ArgumentContainer(names=["--solution-verifier"],
514 kwargs={"type": str,
515 "default": None,
516 "help": "the class name of the solution verifier to use "
517 "for the Solver. If it is a Path, will resolve as "
518 "a SolutionFileVerifier class with the specified "
519 "Path instead."})
521ObjectiveArgument = \
522 ArgumentContainer(names=["--objective"],
523 kwargs={"type": str,
524 "help": "the objective to use."})
526ObjectivesArgument = \
527 ArgumentContainer(names=["--objectives"],
528 kwargs={"type": str,
529 "help": "the comma seperated objective(s) to use."})