variant.py: make Variant.default is str | bool | tuple[str] (#49836)
Warn if variant default is not among those types
This commit is contained in:
@@ -34,11 +34,13 @@ class OpenMpi(Package):
|
||||
import collections.abc
|
||||
import os
|
||||
import re
|
||||
import warnings
|
||||
from typing import Any, Callable, List, Optional, Tuple, Type, Union
|
||||
|
||||
import llnl.util.tty.color
|
||||
|
||||
import spack.deptypes as dt
|
||||
import spack.error
|
||||
import spack.fetch_strategy
|
||||
import spack.package_base
|
||||
import spack.patch
|
||||
@@ -620,7 +622,7 @@ def conditional(*values: List[Any], when: Optional[WhenType] = None):
|
||||
@directive("variants")
|
||||
def variant(
|
||||
name: str,
|
||||
default: Optional[Any] = None,
|
||||
default: Optional[Union[bool, str, Tuple[str, ...]]] = None,
|
||||
description: str = "",
|
||||
values: Optional[Union[collections.abc.Sequence, Callable[[Any], bool]]] = None,
|
||||
multi: Optional[bool] = None,
|
||||
@@ -650,6 +652,24 @@ def variant(
|
||||
DirectiveError: If arguments passed to the directive are invalid
|
||||
"""
|
||||
|
||||
# This validation can be removed at runtime and enforced with an audit in Spack v1.0.
|
||||
# For now it's a warning to let people migrate faster.
|
||||
if not (
|
||||
default is None
|
||||
or type(default) in (bool, str)
|
||||
or (type(default) is tuple and all(type(x) is str for x in default))
|
||||
):
|
||||
if isinstance(default, (list, tuple)):
|
||||
did_you_mean = f"default={','.join(str(x) for x in default)!r}"
|
||||
else:
|
||||
did_you_mean = f"default={str(default)!r}"
|
||||
warnings.warn(
|
||||
f"default value for variant '{name}' is not a boolean or string: default={default!r}. "
|
||||
f"Did you mean {did_you_mean}?",
|
||||
stacklevel=3,
|
||||
category=spack.error.SpackAPIWarning,
|
||||
)
|
||||
|
||||
def format_error(msg, pkg):
|
||||
msg += " @*r{{[{0}, variant '{1}']}}"
|
||||
return llnl.util.tty.color.colorize(msg.format(pkg.name, name))
|
||||
@@ -665,7 +685,11 @@ def _raise_reserved_name(pkg):
|
||||
# Ensure we have a sequence of allowed variant values, or a
|
||||
# predicate for it.
|
||||
if values is None:
|
||||
if str(default).upper() in ("TRUE", "FALSE"):
|
||||
if (
|
||||
default in (True, False)
|
||||
or type(default) is str
|
||||
and default.upper() in ("TRUE", "FALSE")
|
||||
):
|
||||
values = (True, False)
|
||||
else:
|
||||
values = lambda x: True
|
||||
@@ -698,12 +722,15 @@ def _raise_argument_error(pkg):
|
||||
# or the empty string, as the former indicates that a default
|
||||
# was not set while the latter will make the variant unparsable
|
||||
# from the command line
|
||||
if isinstance(default, tuple):
|
||||
default = ",".join(default)
|
||||
|
||||
if default is None or default == "":
|
||||
|
||||
def _raise_default_not_set(pkg):
|
||||
if default is None:
|
||||
msg = "either a default was not explicitly set, " "or 'None' was used"
|
||||
elif default == "":
|
||||
msg = "either a default was not explicitly set, or 'None' was used"
|
||||
else:
|
||||
msg = "the default cannot be an empty string"
|
||||
raise DirectiveError(format_error(msg, pkg))
|
||||
|
||||
|
@@ -64,7 +64,7 @@ class Variant:
|
||||
"""
|
||||
|
||||
name: str
|
||||
default: Any
|
||||
default: Union[bool, str]
|
||||
description: str
|
||||
values: Optional[Collection] #: if None, valid values are defined only by validators
|
||||
multi: bool
|
||||
@@ -77,7 +77,7 @@ def __init__(
|
||||
self,
|
||||
name: str,
|
||||
*,
|
||||
default: Any,
|
||||
default: Union[bool, str],
|
||||
description: str,
|
||||
values: Union[Collection, Callable] = (True, False),
|
||||
multi: bool = False,
|
||||
@@ -200,7 +200,7 @@ def make_default(self):
|
||||
"""
|
||||
return self.make_variant(self.default)
|
||||
|
||||
def make_variant(self, value) -> "AbstractVariant":
|
||||
def make_variant(self, value: Union[str, bool]) -> "AbstractVariant":
|
||||
"""Factory that creates a variant holding the value passed as
|
||||
a parameter.
|
||||
|
||||
@@ -298,7 +298,7 @@ class AbstractVariant:
|
||||
_value: ValueType
|
||||
_original_value: Any
|
||||
|
||||
def __init__(self, name: str, value: Any, propagate: bool = False):
|
||||
def __init__(self, name: str, value: ValueType, propagate: bool = False) -> None:
|
||||
self.name = name
|
||||
self.propagate = propagate
|
||||
|
||||
|
Reference in New Issue
Block a user