1#===----------------------------------------------------------------------===## 2# 3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4# See https://llvm.org/LICENSE.txt for license information. 5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6# 7#===----------------------------------------------------------------------===## 8 9import os 10import pickle 11import pipes 12import platform 13import re 14import shutil 15import tempfile 16 17import libcxx.test.format 18import lit 19import lit.LitConfig 20import lit.Test 21import lit.TestRunner 22import lit.util 23 24class ConfigurationError(Exception): 25 pass 26 27class ConfigurationCompilationError(ConfigurationError): 28 pass 29 30class ConfigurationRuntimeError(ConfigurationError): 31 pass 32 33def _memoizeExpensiveOperation(extractCacheKey): 34 """ 35 Allows memoizing a very expensive operation. 36 37 We pickle the cache key to make sure we store an immutable representation 38 of it. If we stored an object and the object was referenced elsewhere, it 39 could be changed from under our feet, which would break the cache. 40 41 We also store the cache for a given function persistently across invocations 42 of Lit. This dramatically speeds up the configuration of the test suite when 43 invoking Lit repeatedly, which is important for developer workflow. However, 44 with the current implementation that does not synchronize updates to the 45 persistent cache, this also means that one should not call a memoized 46 operation from multiple threads. This should normally not be a problem 47 since Lit configuration is single-threaded. 48 """ 49 def decorator(function): 50 def f(config, *args, **kwargs): 51 cacheRoot = os.path.join(config.test_exec_root, '__config_cache__') 52 persistentCache = os.path.join(cacheRoot, function.__name__) 53 if not os.path.exists(cacheRoot): 54 os.makedirs(cacheRoot) 55 56 cache = {} 57 # Load a cache from a previous Lit invocation if there is one. 58 if os.path.exists(persistentCache): 59 with open(persistentCache, 'rb') as cacheFile: 60 cache = pickle.load(cacheFile) 61 62 cacheKey = pickle.dumps(extractCacheKey(config, *args, **kwargs)) 63 if cacheKey not in cache: 64 cache[cacheKey] = function(config, *args, **kwargs) 65 # Update the persistent cache so it knows about the new key 66 with open(persistentCache, 'wb') as cacheFile: 67 pickle.dump(cache, cacheFile) 68 return cache[cacheKey] 69 return f 70 return decorator 71 72def _executeScriptInternal(test, commands): 73 """ 74 Returns (stdout, stderr, exitCode, timeoutInfo) 75 76 TODO: This really should be easier to access from Lit itself 77 """ 78 parsedCommands = libcxx.test.format.parseScript(test, preamble=commands) 79 80 litConfig = lit.LitConfig.LitConfig( 81 progname='lit', 82 path=[], 83 quiet=False, 84 useValgrind=False, 85 valgrindLeakCheck=False, 86 valgrindArgs=[], 87 noExecute=False, 88 debug=False, 89 isWindows=platform.system() == 'Windows', 90 order='smart', 91 params={}) 92 _, tmpBase = libcxx.test.format._getTempPaths(test) 93 execDir = os.path.dirname(test.getExecPath()) 94 res = lit.TestRunner.executeScriptInternal(test, litConfig, tmpBase, parsedCommands, execDir) 95 if isinstance(res, lit.Test.Result): # Handle failure to parse the Lit test 96 res = ('', res.output, 127, None) 97 (out, err, exitCode, timeoutInfo) = res 98 99 # TODO: As a temporary workaround until https://reviews.llvm.org/D81892 lands, manually 100 # split any stderr output that is included in stdout. It shouldn't be there, but 101 # the Lit internal shell conflates stderr and stdout. 102 conflatedErrorOutput = re.search("(# command stderr:.+$)", out, flags=re.DOTALL) 103 if conflatedErrorOutput: 104 conflatedErrorOutput = conflatedErrorOutput.group(0) 105 out = out[:-len(conflatedErrorOutput)] 106 err += conflatedErrorOutput 107 108 return (out, err, exitCode, timeoutInfo) 109 110def _makeConfigTest(config): 111 # Make sure the support directories exist, which is needed to create 112 # the temporary file %t below. 113 sourceRoot = os.path.join(config.test_exec_root, '__config_src__') 114 execRoot = os.path.join(config.test_exec_root, '__config_exec__') 115 for supportDir in (sourceRoot, execRoot): 116 if not os.path.exists(supportDir): 117 os.makedirs(supportDir) 118 119 # Create a dummy test suite and single dummy test inside it. As part of 120 # the Lit configuration, automatically do the equivalent of 'mkdir %T' 121 # and 'rm -r %T' to avoid cluttering the build directory. 122 suite = lit.Test.TestSuite('__config__', sourceRoot, execRoot, config) 123 tmp = tempfile.NamedTemporaryFile(dir=sourceRoot, delete=False, suffix='.cpp') 124 tmp.close() 125 pathInSuite = [os.path.relpath(tmp.name, sourceRoot)] 126 class TestWrapper(lit.Test.Test): 127 def __enter__(self): 128 testDir, _ = libcxx.test.format._getTempPaths(self) 129 os.makedirs(testDir) 130 return self 131 def __exit__(self, *args): 132 testDir, _ = libcxx.test.format._getTempPaths(self) 133 shutil.rmtree(testDir) 134 os.remove(tmp.name) 135 return TestWrapper(suite, pathInSuite, config) 136 137@_memoizeExpensiveOperation(lambda c, s, f=[]: (c.substitutions, c.environment, s, f)) 138def sourceBuilds(config, source, additionalFlags=[]): 139 """ 140 Return whether the program in the given string builds successfully. 141 142 This is done by compiling and linking a program that consists of the given 143 source with the %{cxx} substitution, and seeing whether that succeeds. If 144 any additional flags are passed, they are appended to the compiler invocation. 145 """ 146 with _makeConfigTest(config) as test: 147 with open(test.getSourcePath(), 'w') as sourceFile: 148 sourceFile.write(source) 149 _, _, exitCode, _ = _executeScriptInternal(test, ['%{{build}} {}'.format(' '.join(additionalFlags))]) 150 return exitCode == 0 151 152@_memoizeExpensiveOperation(lambda c, p, args=None: (c.substitutions, c.environment, p, args)) 153def programOutput(config, program, args=None): 154 """ 155 Compiles a program for the test target, run it on the test target and return 156 the output. 157 158 Note that execution of the program is done through the %{exec} substitution, 159 which means that the program may be run on a remote host depending on what 160 %{exec} does. 161 """ 162 if args is None: 163 args = [] 164 with _makeConfigTest(config) as test: 165 with open(test.getSourcePath(), 'w') as source: 166 source.write(program) 167 _, err, exitCode, _ = _executeScriptInternal(test, ['%{build}']) 168 if exitCode != 0: 169 raise ConfigurationCompilationError("Failed to build program, stderr is:\n{}".format(err)) 170 171 out, err, exitCode, _ = _executeScriptInternal(test, ["%{{run}} {}".format(' '.join(args))]) 172 if exitCode != 0: 173 raise ConfigurationRuntimeError("Failed to run program, stderr is:\n{}".format(err)) 174 175 actualOut = re.search("# command output:\n(.+)\n$", out, flags=re.DOTALL) 176 actualOut = actualOut.group(1) if actualOut else "" 177 return actualOut 178 179@_memoizeExpensiveOperation(lambda c, p, args=None: (c.substitutions, c.environment, p, args)) 180def programSucceeds(config, program, args=None): 181 """ 182 Compiles a program for the test target, run it on the test target and return 183 whether it completed successfully. 184 185 Note that execution of the program is done through the %{exec} substitution, 186 which means that the program may be run on a remote host depending on what 187 %{exec} does. 188 """ 189 try: 190 programOutput(config, program, args) 191 except ConfigurationRuntimeError: 192 return False 193 return True 194 195@_memoizeExpensiveOperation(lambda c, f: (c.substitutions, c.environment, f)) 196def hasCompileFlag(config, flag): 197 """ 198 Return whether the compiler in the configuration supports a given compiler flag. 199 200 This is done by executing the %{cxx} substitution with the given flag and 201 checking whether that succeeds. 202 """ 203 with _makeConfigTest(config) as test: 204 out, err, exitCode, timeoutInfo = _executeScriptInternal(test, [ 205 "%{{cxx}} -xc++ {} -Werror -fsyntax-only %{{flags}} %{{compile_flags}} {}".format(os.devnull, flag) 206 ]) 207 return exitCode == 0 208 209@_memoizeExpensiveOperation(lambda c, s: (c.substitutions, c.environment, s)) 210def runScriptExitCode(config, script): 211 """ 212 Runs the given script as a Lit test, and returns the exit code of the execution. 213 214 The script must be a list of commands, each of which being something that 215 could appear on the right-hand-side of a `RUN:` keyword. 216 """ 217 with _makeConfigTest(config) as test: 218 _, _, exitCode, _ = _executeScriptInternal(test, script) 219 return exitCode 220 221@_memoizeExpensiveOperation(lambda c, s: (c.substitutions, c.environment, s)) 222def commandOutput(config, command): 223 """ 224 Runs the given script as a Lit test, and returns the output. 225 If the exit code isn't 0 an exception is raised. 226 227 The script must be a list of commands, each of which being something that 228 could appear on the right-hand-side of a `RUN:` keyword. 229 """ 230 with _makeConfigTest(config) as test: 231 out, _, exitCode, _ = _executeScriptInternal(test, command) 232 if exitCode != 0: 233 raise ConfigurationRuntimeError() 234 return out 235 236@_memoizeExpensiveOperation(lambda c, l: (c.substitutions, c.environment, l)) 237def hasAnyLocale(config, locales): 238 """ 239 Return whether the runtime execution environment supports a given locale. 240 Different systems may use different names for a locale, so this function checks 241 whether any of the passed locale names is supported by setlocale() and returns 242 true if one of them works. 243 244 This is done by executing a program that tries to set the given locale using 245 %{exec} -- this means that the command may be executed on a remote host 246 depending on the %{exec} substitution. 247 """ 248 program = """ 249 #include <stddef.h> 250 #if defined(_LIBCPP_HAS_NO_LOCALIZATION) 251 int main(int, char**) { return 1; } 252 #else 253 #include <locale.h> 254 int main(int argc, char** argv) { 255 for (int i = 1; i < argc; i++) { 256 if (::setlocale(LC_ALL, argv[i]) != NULL) { 257 return 0; 258 } 259 } 260 return 1; 261 } 262 #endif 263 """ 264 return programSucceeds(config, program, args=[pipes.quote(l) for l in locales]) 265 266@_memoizeExpensiveOperation(lambda c, flags='': (c.substitutions, c.environment, flags)) 267def compilerMacros(config, flags=''): 268 """ 269 Return a dictionary of predefined compiler macros. 270 271 The keys are strings representing macros, and the values are strings 272 representing what each macro is defined to. 273 274 If the optional `flags` argument (a string) is provided, these flags will 275 be added to the compiler invocation when generating the macros. 276 """ 277 with _makeConfigTest(config) as test: 278 with open(test.getSourcePath(), 'w') as sourceFile: 279 sourceFile.write(""" 280 #if __has_include(<__config_site>) 281 # include <__config_site> 282 #endif 283 """) 284 unparsedOutput, err, exitCode, _ = _executeScriptInternal(test, [ 285 "%{{cxx}} %s -dM -E %{{flags}} %{{compile_flags}} {}".format(flags) 286 ]) 287 if exitCode != 0: 288 raise ConfigurationCompilationError("Failed to retrieve compiler macros, stderr is:\n{}".format(err)) 289 parsedMacros = dict() 290 defines = (l.strip() for l in unparsedOutput.split('\n') if l.startswith('#define ')) 291 for line in defines: 292 line = line[len('#define '):] 293 macro, _, value = line.partition(' ') 294 parsedMacros[macro] = value 295 return parsedMacros 296 297def featureTestMacros(config, flags=''): 298 """ 299 Return a dictionary of feature test macros. 300 301 The keys are strings representing feature test macros, and the values are 302 integers representing the value of the macro. 303 """ 304 allMacros = compilerMacros(config, flags) 305 return {m: int(v.rstrip('LlUu')) for (m, v) in allMacros.items() if m.startswith('__cpp_')} 306 307def _appendToSubstitution(substitutions, key, value): 308 return [(k, v + ' ' + value) if k == key else (k, v) for (k, v) in substitutions] 309 310def _prependToSubstitution(substitutions, key, value): 311 return [(k, value + ' ' + v) if k == key else (k, v) for (k, v) in substitutions] 312 313 314class ConfigAction(object): 315 """ 316 This class represents an action that can be performed on a Lit TestingConfig 317 object. 318 319 Examples of such actions are adding or modifying substitutions, Lit features, 320 etc. This class only provides the interface of such actions, and it is meant 321 to be subclassed appropriately to create new actions. 322 """ 323 def applyTo(self, config): 324 """ 325 Applies the action to the given configuration. 326 327 This should modify the configuration object in place, and return nothing. 328 329 If applying the action to the configuration would yield an invalid 330 configuration, and it is possible to diagnose it here, this method 331 should produce an error. For example, it should be an error to modify 332 a substitution in a way that we know for sure is invalid (e.g. adding 333 a compiler flag when we know the compiler doesn't support it). Failure 334 to do so early may lead to difficult-to-diagnose issues down the road. 335 """ 336 pass 337 338 def pretty(self, config, litParams): 339 """ 340 Returns a short and human-readable string describing what this action does. 341 342 This is used for logging purposes when running the test suite, so it should 343 be kept concise. 344 """ 345 pass 346 347 348class AddFeature(ConfigAction): 349 """ 350 This action defines the given Lit feature when running the test suite. 351 352 The name of the feature can be a string or a callable, in which case it is 353 called with the configuration to produce the feature name (as a string). 354 """ 355 def __init__(self, name): 356 self._name = name 357 358 def _getName(self, config): 359 name = self._name(config) if callable(self._name) else self._name 360 if not isinstance(name, str): 361 raise ValueError("Lit feature did not resolve to a string (got {})".format(name)) 362 return name 363 364 def applyTo(self, config): 365 config.available_features.add(self._getName(config)) 366 367 def pretty(self, config, litParams): 368 return 'add Lit feature {}'.format(self._getName(config)) 369 370 371class AddFlag(ConfigAction): 372 """ 373 This action adds the given flag to the %{flags} substitution. 374 375 The flag can be a string or a callable, in which case it is called with the 376 configuration to produce the actual flag (as a string). 377 """ 378 def __init__(self, flag): 379 self._getFlag = lambda config: flag(config) if callable(flag) else flag 380 381 def applyTo(self, config): 382 flag = self._getFlag(config) 383 assert hasCompileFlag(config, flag), "Trying to enable flag {}, which is not supported".format(flag) 384 config.substitutions = _appendToSubstitution(config.substitutions, '%{flags}', flag) 385 386 def pretty(self, config, litParams): 387 return 'add {} to %{{flags}}'.format(self._getFlag(config)) 388 389 390class AddFlagIfSupported(ConfigAction): 391 """ 392 This action adds the given flag to the %{flags} substitution, only if 393 the compiler supports the flag. 394 395 The flag can be a string or a callable, in which case it is called with the 396 configuration to produce the actual flag (as a string). 397 """ 398 def __init__(self, flag): 399 self._getFlag = lambda config: flag(config) if callable(flag) else flag 400 401 def applyTo(self, config): 402 flag = self._getFlag(config) 403 if hasCompileFlag(config, flag): 404 config.substitutions = _appendToSubstitution(config.substitutions, '%{flags}', flag) 405 406 def pretty(self, config, litParams): 407 return 'add {} to %{{flags}}'.format(self._getFlag(config)) 408 409 410class AddCompileFlag(ConfigAction): 411 """ 412 This action adds the given flag to the %{compile_flags} substitution. 413 414 The flag can be a string or a callable, in which case it is called with the 415 configuration to produce the actual flag (as a string). 416 """ 417 def __init__(self, flag): 418 self._getFlag = lambda config: flag(config) if callable(flag) else flag 419 420 def applyTo(self, config): 421 flag = self._getFlag(config) 422 assert hasCompileFlag(config, flag), "Trying to enable compile flag {}, which is not supported".format(flag) 423 config.substitutions = _appendToSubstitution(config.substitutions, '%{compile_flags}', flag) 424 425 def pretty(self, config, litParams): 426 return 'add {} to %{{compile_flags}}'.format(self._getFlag(config)) 427 428 429class AddLinkFlag(ConfigAction): 430 """ 431 This action appends the given flag to the %{link_flags} substitution. 432 433 The flag can be a string or a callable, in which case it is called with the 434 configuration to produce the actual flag (as a string). 435 """ 436 def __init__(self, flag): 437 self._getFlag = lambda config: flag(config) if callable(flag) else flag 438 439 def applyTo(self, config): 440 flag = self._getFlag(config) 441 assert hasCompileFlag(config, flag), "Trying to enable link flag {}, which is not supported".format(flag) 442 config.substitutions = _appendToSubstitution(config.substitutions, '%{link_flags}', flag) 443 444 def pretty(self, config, litParams): 445 return 'append {} to %{{link_flags}}'.format(self._getFlag(config)) 446 447 448class PrependLinkFlag(ConfigAction): 449 """ 450 This action prepends the given flag to the %{link_flags} substitution. 451 452 The flag can be a string or a callable, in which case it is called with the 453 configuration to produce the actual flag (as a string). 454 """ 455 def __init__(self, flag): 456 self._getFlag = lambda config: flag(config) if callable(flag) else flag 457 458 def applyTo(self, config): 459 flag = self._getFlag(config) 460 assert hasCompileFlag(config, flag), "Trying to enable link flag {}, which is not supported".format(flag) 461 config.substitutions = _prependToSubstitution(config.substitutions, '%{link_flags}', flag) 462 463 def pretty(self, config, litParams): 464 return 'prepend {} to %{{link_flags}}'.format(self._getFlag(config)) 465 466 467class AddOptionalWarningFlag(ConfigAction): 468 """ 469 This action adds the given warning flag to the %{compile_flags} substitution, 470 if it is supported by the compiler. 471 472 The flag can be a string or a callable, in which case it is called with the 473 configuration to produce the actual flag (as a string). 474 """ 475 def __init__(self, flag): 476 self._getFlag = lambda config: flag(config) if callable(flag) else flag 477 478 def applyTo(self, config): 479 flag = self._getFlag(config) 480 # Use -Werror to make sure we see an error about the flag being unsupported. 481 if hasCompileFlag(config, '-Werror ' + flag): 482 config.substitutions = _appendToSubstitution(config.substitutions, '%{compile_flags}', flag) 483 484 def pretty(self, config, litParams): 485 return 'add {} to %{{compile_flags}}'.format(self._getFlag(config)) 486 487 488class AddSubstitution(ConfigAction): 489 """ 490 This action adds the given substitution to the Lit configuration. 491 492 The substitution can be a string or a callable, in which case it is called 493 with the configuration to produce the actual substitution (as a string). 494 """ 495 def __init__(self, key, substitution): 496 self._key = key 497 self._getSub = lambda config: substitution(config) if callable(substitution) else substitution 498 499 def applyTo(self, config): 500 key = self._key 501 sub = self._getSub(config) 502 config.substitutions.append((key, sub)) 503 504 def pretty(self, config, litParams): 505 return 'add substitution {} = {}'.format(self._key, self._getSub(config)) 506 507 508class Feature(object): 509 """ 510 Represents a Lit available feature that is enabled whenever it is supported. 511 512 A feature like this informs the test suite about a capability of the compiler, 513 platform, etc. Unlike Parameters, it does not make sense to explicitly 514 control whether a Feature is enabled -- it should be enabled whenever it 515 is supported. 516 """ 517 def __init__(self, name, actions=None, when=lambda _: True): 518 """ 519 Create a Lit feature for consumption by a test suite. 520 521 - name 522 The name of the feature. This is what will end up in Lit's available 523 features if the feature is enabled. This can be either a string or a 524 callable, in which case it is passed the TestingConfig and should 525 generate a string representing the name of the feature. 526 527 - actions 528 An optional list of ConfigActions to apply when the feature is supported. 529 An AddFeature action is always created regardless of any actions supplied 530 here -- these actions are meant to perform more than setting a corresponding 531 Lit feature (e.g. adding compiler flags). If 'actions' is a callable, it 532 is called with the current configuration object to generate the actual 533 list of actions. 534 535 - when 536 A callable that gets passed a TestingConfig and should return a 537 boolean representing whether the feature is supported in that 538 configuration. For example, this can use `hasCompileFlag` to 539 check whether the compiler supports the flag that the feature 540 represents. If omitted, the feature will always be considered 541 supported. 542 """ 543 self._name = name 544 self._actions = [] if actions is None else actions 545 self._isSupported = when 546 547 def _getName(self, config): 548 name = self._name(config) if callable(self._name) else self._name 549 if not isinstance(name, str): 550 raise ValueError("Feature did not resolve to a name that's a string, got {}".format(name)) 551 return name 552 553 def getActions(self, config): 554 """ 555 Return the list of actions associated to this feature. 556 557 If the feature is not supported, an empty list is returned. 558 If the feature is supported, an `AddFeature` action is automatically added 559 to the returned list of actions, in addition to any actions provided on 560 construction. 561 """ 562 if not self._isSupported(config): 563 return [] 564 else: 565 actions = self._actions(config) if callable(self._actions) else self._actions 566 return [AddFeature(self._getName(config))] + actions 567 568 def pretty(self, config): 569 """ 570 Returns the Feature's name. 571 """ 572 return self._getName(config) 573 574 575def _str_to_bool(s): 576 """ 577 Convert a string value to a boolean. 578 579 True values are "y", "yes", "t", "true", "on" and "1", regardless of capitalization. 580 False values are "n", "no", "f", "false", "off" and "0", regardless of capitalization. 581 """ 582 trueVals = ["y", "yes", "t", "true", "on", "1"] 583 falseVals = ["n", "no", "f", "false", "off", "0"] 584 lower = s.lower() 585 if lower in trueVals: 586 return True 587 elif lower in falseVals: 588 return False 589 else: 590 raise ValueError("Got string '{}', which isn't a valid boolean".format(s)) 591 592def _parse_parameter(s, type): 593 if type is bool and isinstance(s, str): 594 return _str_to_bool(s) 595 elif type is list and isinstance(s, str): 596 return [x.strip() for x in s.split(',') if x.strip()] 597 return type(s) 598 599 600class Parameter(object): 601 """ 602 Represents a parameter of a Lit test suite. 603 604 Parameters are used to customize the behavior of test suites in a user 605 controllable way. There are two ways of setting the value of a Parameter. 606 The first one is to pass `--param <KEY>=<VALUE>` when running Lit (or 607 equivalently to set `litConfig.params[KEY] = VALUE` somewhere in the 608 Lit configuration files. This method will set the parameter globally for 609 all test suites being run. 610 611 The second method is to set `config.KEY = VALUE` somewhere in the Lit 612 configuration files, which sets the parameter only for the test suite(s) 613 that use that `config` object. 614 615 Parameters can have multiple possible values, and they can have a default 616 value when left unspecified. They can also have any number of ConfigActions 617 associated to them, in which case the actions will be performed on the 618 TestingConfig if the parameter is enabled. Depending on the actions 619 associated to a Parameter, it may be an error to enable the Parameter 620 if some actions are not supported in the given configuration. For example, 621 trying to set the compilation standard to C++23 when `-std=c++23` is not 622 supported by the compiler would be an error. 623 """ 624 def __init__(self, name, type, help, actions, choices=None, default=None): 625 """ 626 Create a Lit parameter to customize the behavior of a test suite. 627 628 - name 629 The name of the parameter that can be used to set it on the command-line. 630 On the command-line, the parameter can be set using `--param <name>=<value>` 631 when running Lit. This must be non-empty. 632 633 - choices 634 An optional non-empty set of possible values for this parameter. If provided, 635 this must be anything that can be iterated. It is an error if the parameter 636 is given a value that is not in that set, whether explicitly or through a 637 default value. 638 639 - type 640 A callable that can be used to parse the value of the parameter given 641 on the command-line. As a special case, using the type `bool` also 642 allows parsing strings with boolean-like contents, and the type `list` 643 will parse a string delimited by commas into a list of the substrings. 644 645 - help 646 A string explaining the parameter, for documentation purposes. 647 TODO: We should be able to surface those from the Lit command-line. 648 649 - actions 650 A callable that gets passed the parsed value of the parameter (either 651 the one passed on the command-line or the default one), and that returns 652 a list of ConfigAction to perform given the value of the parameter. 653 All the ConfigAction must be supported in the given configuration. 654 655 - default 656 An optional default value to use for the parameter when no value is 657 provided on the command-line. If the default value is a callable, it 658 is called with the TestingConfig and should return the default value 659 for the parameter. Whether the default value is computed or specified 660 directly, it must be in the 'choices' provided for that Parameter. 661 """ 662 self._name = name 663 if len(self._name) == 0: 664 raise ValueError("Parameter name must not be the empty string") 665 666 if choices is not None: 667 self._choices = list(choices) # should be finite 668 if len(self._choices) == 0: 669 raise ValueError("Parameter '{}' must be given at least one possible value".format(self._name)) 670 else: 671 self._choices = None 672 673 self._parse = lambda x: _parse_parameter(x, type) 674 self._help = help 675 self._actions = actions 676 self._default = default 677 678 def _getValue(self, config, litParams): 679 """ 680 Return the value of the parameter given the configuration objects. 681 """ 682 param = getattr(config, self.name, None) 683 param = litParams.get(self.name, param) 684 if param is None and self._default is None: 685 raise ValueError("Parameter {} doesn't have a default value, but it was not specified in the Lit parameters or in the Lit config".format(self.name)) 686 getDefault = lambda: self._default(config) if callable(self._default) else self._default 687 688 if param is not None: 689 (pretty, value) = (param, self._parse(param)) 690 else: 691 value = getDefault() 692 pretty = '{} (default)'.format(value) 693 694 if self._choices and value not in self._choices: 695 raise ValueError("Got value '{}' for parameter '{}', which is not in the provided set of possible choices: {}".format(value, self.name, self._choices)) 696 return (pretty, value) 697 698 @property 699 def name(self): 700 """ 701 Return the name of the parameter. 702 703 This is the name that can be used to set the parameter on the command-line 704 when running Lit. 705 """ 706 return self._name 707 708 def getActions(self, config, litParams): 709 """ 710 Return the list of actions associated to this value of the parameter. 711 """ 712 (_, parameterValue) = self._getValue(config, litParams) 713 return self._actions(parameterValue) 714 715 def pretty(self, config, litParams): 716 """ 717 Return a pretty representation of the parameter's name and value. 718 """ 719 (prettyParameterValue, _) = self._getValue(config, litParams) 720 return "{}={}".format(self.name, prettyParameterValue) 721