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:
Harmen Stoppels 2025-04-03 13:07:08 +02:00 committed by GitHub
parent 22f26eec68
commit 751c79872f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 69 additions and 37 deletions

View File

@ -34,11 +34,13 @@ class OpenMpi(Package):
import collections.abc import collections.abc
import os import os
import re import re
import warnings
from typing import Any, Callable, List, Optional, Tuple, Type, Union from typing import Any, Callable, List, Optional, Tuple, Type, Union
import llnl.util.tty.color import llnl.util.tty.color
import spack.deptypes as dt import spack.deptypes as dt
import spack.error
import spack.fetch_strategy import spack.fetch_strategy
import spack.package_base import spack.package_base
import spack.patch import spack.patch
@ -620,7 +622,7 @@ def conditional(*values: List[Any], when: Optional[WhenType] = None):
@directive("variants") @directive("variants")
def variant( def variant(
name: str, name: str,
default: Optional[Any] = None, default: Optional[Union[bool, str, Tuple[str, ...]]] = None,
description: str = "", description: str = "",
values: Optional[Union[collections.abc.Sequence, Callable[[Any], bool]]] = None, values: Optional[Union[collections.abc.Sequence, Callable[[Any], bool]]] = None,
multi: Optional[bool] = None, multi: Optional[bool] = None,
@ -650,6 +652,24 @@ def variant(
DirectiveError: If arguments passed to the directive are invalid 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): def format_error(msg, pkg):
msg += " @*r{{[{0}, variant '{1}']}}" msg += " @*r{{[{0}, variant '{1}']}}"
return llnl.util.tty.color.colorize(msg.format(pkg.name, name)) 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 # Ensure we have a sequence of allowed variant values, or a
# predicate for it. # predicate for it.
if values is None: 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) values = (True, False)
else: else:
values = lambda x: True values = lambda x: True
@ -698,12 +722,15 @@ def _raise_argument_error(pkg):
# or the empty string, as the former indicates that a default # or the empty string, as the former indicates that a default
# was not set while the latter will make the variant unparsable # was not set while the latter will make the variant unparsable
# from the command line # from the command line
if isinstance(default, tuple):
default = ",".join(default)
if default is None or default == "": if default is None or default == "":
def _raise_default_not_set(pkg): def _raise_default_not_set(pkg):
if default is None: if default is None:
msg = "either a default was not explicitly set, " "or 'None' was used" msg = "either a default was not explicitly set, or 'None' was used"
elif default == "": else:
msg = "the default cannot be an empty string" msg = "the default cannot be an empty string"
raise DirectiveError(format_error(msg, pkg)) raise DirectiveError(format_error(msg, pkg))

View File

@ -64,7 +64,7 @@ class Variant:
""" """
name: str name: str
default: Any default: Union[bool, str]
description: str description: str
values: Optional[Collection] #: if None, valid values are defined only by validators values: Optional[Collection] #: if None, valid values are defined only by validators
multi: bool multi: bool
@ -77,7 +77,7 @@ def __init__(
self, self,
name: str, name: str,
*, *,
default: Any, default: Union[bool, str],
description: str, description: str,
values: Union[Collection, Callable] = (True, False), values: Union[Collection, Callable] = (True, False),
multi: bool = False, multi: bool = False,
@ -200,7 +200,7 @@ def make_default(self):
""" """
return self.make_variant(self.default) 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 """Factory that creates a variant holding the value passed as
a parameter. a parameter.
@ -298,7 +298,7 @@ class AbstractVariant:
_value: ValueType _value: ValueType
_original_value: Any _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.name = name
self.propagate = propagate self.propagate = propagate

View File

@ -37,7 +37,7 @@ class Abyss(AutotoolsPackage):
version("1.5.2", sha256="8a52387f963afb7b63db4c9b81c053ed83956ea0a3981edcad554a895adf84b1") version("1.5.2", sha256="8a52387f963afb7b63db4c9b81c053ed83956ea0a3981edcad554a895adf84b1")
variant( variant(
"maxk", default=128, values=is_multiple_32, description="set the maximum k-mer length." "maxk", default="128", values=is_multiple_32, description="set the maximum k-mer length."
) )
depends_on("c", type="build") depends_on("c", type="build")

View File

@ -32,7 +32,7 @@ class Asagi(CMakePackage):
) )
variant("fortran", default=True, description="enable fortran support") variant("fortran", default=True, description="enable fortran support")
variant("max_dimensions", default=4, description="max. number of dimensions supported") variant("max_dimensions", default="4", description="max. number of dimensions supported")
variant("numa", default=True, description="enable NUMA support") variant("numa", default=True, description="enable NUMA support")
variant("mpi", default=True, description="enable MPI") variant("mpi", default=True, description="enable MPI")
variant("threadsafe", default=True, description="enable threadsafe ASAGI-functions") variant("threadsafe", default=True, description="enable threadsafe ASAGI-functions")

View File

@ -85,7 +85,7 @@ class Athena(AutotoolsPackage):
description="Equation of state", description="Equation of state",
values=["adiabatic", "isothermal"], values=["adiabatic", "isothermal"],
) )
variant("nscalars", default=0, description="Number of advected scalars") variant("nscalars", default="0", description="Number of advected scalars")
variant( variant(
"gravity", "gravity",
default="none", default="none",

View File

@ -59,9 +59,9 @@ class Atlas(Package):
variant( variant(
"tune_cpu", "tune_cpu",
default=-1, default="-1",
multi=False, multi=False,
description="Number of threads to tune to, " "-1 for autodetect, 0 for no threading", description="Number of threads to tune to, -1 for autodetect, 0 for no threading",
) )
conflicts( conflicts(

View File

@ -41,7 +41,7 @@ class CcsQcd(MakefilePackage):
variant( variant(
"class", "class",
default=1, default="1",
values=class_validator, values=class_validator,
description="This miniapp has five problem classes, for which the" description="This miniapp has five problem classes, for which the"
" first three are relatively small problems just for testing" " first three are relatively small problems just for testing"

View File

@ -25,7 +25,7 @@ class FenicsDolfinx(CMakePackage):
variant( variant(
"partitioners", "partitioners",
description="Graph partioning", description="Graph partioning",
default=("parmetis",), default="parmetis",
values=("kahip", "parmetis", "scotch"), values=("kahip", "parmetis", "scotch"),
multi=True, multi=True,
) )

View File

@ -38,7 +38,7 @@ class G2(CMakePackage):
when="@3.4.6:", when="@3.4.6:",
) )
variant("w3emc", default=True, description="Enable GRIB1 through w3emc", when="@3.4.6:") variant("w3emc", default=True, description="Enable GRIB1 through w3emc", when="@3.4.6:")
variant("shared", default="False", description="Build shared library", when="@3.4.7:") variant("shared", default=False, description="Build shared library", when="@3.4.7:")
variant("openmp", default=False, description="Use OpenMP multithreading", when="@develop") variant("openmp", default=False, description="Use OpenMP multithreading", when="@develop")
variant("utils", default=False, description="Build grib utilities", when="@develop") variant("utils", default=False, description="Build grib utilities", when="@develop")
variant( variant(

View File

@ -31,7 +31,7 @@ class G2c(CMakePackage):
variant("pic", default=True, description="Build with position-independent-code") variant("pic", default=True, description="Build with position-independent-code")
variant( variant(
"libs", "libs",
default=("shared", "static"), default="shared,static",
values=("shared", "static"), values=("shared", "static"),
multi=True, multi=True,
description="Build shared libs, static libs or both", description="Build shared libs, static libs or both",

View File

@ -344,19 +344,19 @@ def validate_gasnet_root(value):
variant( variant(
"max_dims", "max_dims",
values=int, values=int,
default=3, default="3",
description="Set max number of dimensions for logical regions.", description="Set max number of dimensions for logical regions.",
) )
variant( variant(
"max_fields", "max_fields",
values=int, values=int,
default=512, default="512",
description="Maximum number of fields allowed in a logical region.", description="Maximum number of fields allowed in a logical region.",
) )
variant( variant(
"max_num_nodes", "max_num_nodes",
values=int, values=int,
default=1024, default="1024",
description="Maximum number of nodes supported by Legion.", description="Maximum number of nodes supported by Legion.",
) )
variant("prof", default=False, description="Install Rust Legion prof") variant("prof", default=False, description="Install Rust Legion prof")

View File

@ -68,7 +68,7 @@ class LibjpegTurbo(CMakePackage, AutotoolsPackage):
variant( variant(
"libs", "libs",
default=("shared", "static"), default="shared,static",
values=("shared", "static"), values=("shared", "static"),
multi=True, multi=True,
description="Build shared libs, static libs, or both", description="Build shared libs, static libs, or both",

View File

@ -24,14 +24,14 @@ class MpiSerial(AutotoolsPackage):
variant( variant(
"fort-real-size", "fort-real-size",
values=int, values=int,
default=4, default="4",
description="Specify the size of Fortran real variables", description="Specify the size of Fortran real variables",
) )
variant( variant(
"fort-double-size", "fort-double-size",
values=int, values=int,
default=8, default="8",
description="Specify the size of Fortran double precision variables", description="Specify the size of Fortran double precision variables",
) )

View File

@ -17,7 +17,7 @@ class Mpileaks(AutotoolsPackage):
variant( variant(
"stackstart", "stackstart",
values=int, values=int,
default=0, default="0",
description="Specify the number of stack frames to truncate", description="Specify the number of stack frames to truncate",
) )

View File

@ -35,18 +35,21 @@ class Mpip(AutotoolsPackage):
variant( variant(
"maxargs", "maxargs",
values=int, values=int,
default=32, default="32",
description="Set number of command line arguments in report", description="Set number of command line arguments in report",
) )
variant( variant(
"stackdepth", values=int, default=8, description="Specify maximum report stacktrace depth" "stackdepth",
values=int,
default="8",
description="Specify maximum report stacktrace depth",
) )
variant( variant(
"internal_stackdepth", "internal_stackdepth",
values=int, values=int,
default=3, default="3",
description="Specify number of internal stack frames", description="Specify number of internal stack frames",
) )

View File

@ -43,13 +43,13 @@ class NaluWind(CMakePackage, CudaPackage, ROCmPackage):
variant("pic", default=True, description="Position independent code") variant("pic", default=True, description="Position independent code")
variant( variant(
"abs_tol", "abs_tol",
default=1.0e-15, default="1.0e-15",
values=_parse_float, values=_parse_float,
description="Absolute tolerance for regression tests", description="Absolute tolerance for regression tests",
) )
variant( variant(
"rel_tol", "rel_tol",
default=1.0e-12, default="1.0e-12",
values=_parse_float, values=_parse_float,
description="Relative tolerance for regression tests", description="Relative tolerance for regression tests",
) )

View File

@ -42,7 +42,7 @@ class Nektools(Package):
# Variant for MAXNEL, we need to read this from user # Variant for MAXNEL, we need to read this from user
variant( variant(
"MAXNEL", "MAXNEL",
default=150000, default="150000",
description="Maximum number of elements for Nek5000 tools.", description="Maximum number of elements for Nek5000 tools.",
values=is_integral, values=is_integral,
) )

View File

@ -247,7 +247,7 @@ class Openloops(Package):
description="Number of parallel jobs to run. " description="Number of parallel jobs to run. "
+ "Set to 1 if compiling a large number" + "Set to 1 if compiling a large number"
+ "of processes (e.g. lcg.coll)", + "of processes (e.g. lcg.coll)",
default=0, default="0",
) )
depends_on("python", type=("build", "run")) depends_on("python", type=("build", "run"))

View File

@ -37,9 +37,11 @@ class Psblas(AutotoolsPackage):
# Variants: # Variants:
# LPK/IPK: Integer precision variants # LPK/IPK: Integer precision variants
variant("LPK", default=8, values=int, description="Length in bytes for long integers (8 or 4)")
variant( variant(
"IPK", default=4, values=int, description="Length in bytes for short integers (8 or 4)" "LPK", default="8", values=int, description="Length in bytes for long integers (8 or 4)"
)
variant(
"IPK", default="4", values=int, description="Length in bytes for short integers (8 or 4)"
) )
# MPI # MPI
variant("mpi", default=True, description="Activates MPI support") variant("mpi", default=True, description="Activates MPI support")

View File

@ -49,7 +49,7 @@ class Pythia6(CMakePackage):
# intended to be used with other code with different requirements. # intended to be used with other code with different requirements.
variant( variant(
"nmxhep", "nmxhep",
default=4000, default="4000",
values=_is_integral, values=_is_integral,
description="Extent of particle arrays in the /HEPEVT/ COMMON block.", description="Extent of particle arrays in the /HEPEVT/ COMMON block.",
) )

View File

@ -59,7 +59,7 @@ class Qthreads(AutotoolsPackage):
variant("static", default=True, description="Build static library") variant("static", default=True, description="Build static library")
variant( variant(
"stack_size", "stack_size",
default=4096, default="4096",
description="Specify number of bytes to use in a stack", description="Specify number of bytes to use in a stack",
values=is_integer, values=is_integer,
) )

View File

@ -88,7 +88,7 @@ class Quda(CMakePackage, CudaPackage, ROCmPackage):
with when("+multigrid"): with when("+multigrid"):
variant( variant(
"mg_mrhs_list", "mg_mrhs_list",
default=16, default="16",
multi=True, multi=True,
description="The list of multi-rhs sizes that get compiled", description="The list of multi-rhs sizes that get compiled",
) )

View File

@ -29,7 +29,7 @@ class Soci(CMakePackage):
variant( variant(
"cxxstd", "cxxstd",
default=11, default="11",
values=("98", "11", "14", "17", "20"), values=("98", "11", "14", "17", "20"),
multi=False, multi=False,
description="Use the specified C++ standard when building", description="Use the specified C++ standard when building",