diff --git a/lib/spack/spack/schema/__init__.py b/lib/spack/spack/schema/__init__.py index 83e8c06b246..45474dfdc78 100644 --- a/lib/spack/spack/schema/__init__.py +++ b/lib/spack/spack/schema/__init__.py @@ -7,8 +7,7 @@ import warnings import jsonschema - -import llnl.util.lang +import jsonschema.validators from spack.error import SpecSyntaxError @@ -18,59 +17,59 @@ class DeprecationMessage(typing.NamedTuple): error: bool -# jsonschema is imported lazily as it is heavy to import -# and increases the start-up time -def _make_validator(): - def _validate_spec(validator, is_spec, instance, schema): - """Check if the attributes on instance are valid specs.""" - import spack.spec_parser +def _validate_spec(validator, is_spec, instance, schema): + """Check if all additional keys are valid specs.""" + import spack.spec_parser - if not validator.is_type(instance, "object"): - return + if not validator.is_type(instance, "object"): + return - for spec_str in instance: - try: - spack.spec_parser.parse(spec_str) - except SpecSyntaxError: - yield jsonschema.ValidationError(f"the key '{spec_str}' is not a valid spec") + properties = schema.get("properties") or {} - def _deprecated_properties(validator, deprecated, instance, schema): - if not (validator.is_type(instance, "object") or validator.is_type(instance, "array")): - return - - if not deprecated: - return - - deprecations = { - name: DeprecationMessage(message=x["message"], error=x["error"]) - for x in deprecated - for name in x["names"] - } - - # Get a list of the deprecated properties, return if there is none - issues = [entry for entry in instance if entry in deprecations] - if not issues: - return - - # Process issues - errors = [] - for name in issues: - msg = deprecations[name].message.format(name=name) - if deprecations[name].error: - errors.append(msg) - else: - warnings.warn(msg) - - if errors: - yield jsonschema.ValidationError("\n".join(errors)) - - return jsonschema.validators.extend( - jsonschema.Draft7Validator, - {"validate_spec": _validate_spec, "deprecatedProperties": _deprecated_properties}, - ) + for spec_str in instance: + if spec_str in properties: + continue + try: + spack.spec_parser.parse(spec_str) + except SpecSyntaxError: + yield jsonschema.ValidationError(f"the key '{spec_str}' is not a valid spec") -Validator = llnl.util.lang.Singleton(_make_validator) +def _deprecated_properties(validator, deprecated, instance, schema): + if not (validator.is_type(instance, "object") or validator.is_type(instance, "array")): + return + + if not deprecated: + return + + deprecations = { + name: DeprecationMessage(message=x["message"], error=x["error"]) + for x in deprecated + for name in x["names"] + } + + # Get a list of the deprecated properties, return if there is none + issues = [entry for entry in instance if entry in deprecations] + if not issues: + return + + # Process issues + errors = [] + for name in issues: + msg = deprecations[name].message.format(name=name) + if deprecations[name].error: + errors.append(msg) + else: + warnings.warn(msg) + + if errors: + yield jsonschema.ValidationError("\n".join(errors)) + + +Validator = jsonschema.validators.extend( + jsonschema.Draft7Validator, + {"additionalKeysAreSpecs": _validate_spec, "deprecatedProperties": _deprecated_properties}, +) def _append(string: str) -> bool: diff --git a/lib/spack/spack/schema/modules.py b/lib/spack/spack/schema/modules.py index f8c1e76087a..4662c67f863 100644 --- a/lib/spack/spack/schema/modules.py +++ b/lib/spack/spack/schema/modules.py @@ -39,7 +39,7 @@ "load": array_of_strings, "suffixes": { "type": "object", - "validate_spec": True, + "additionalKeysAreSpecs": True, "additionalProperties": {"type": "string"}, # key }, "environment": spack.schema.environment.definition, @@ -64,7 +64,7 @@ tcl_configuration = { "type": "object", "default": {}, - "validate_spec": True, + "additionalKeysAreSpecs": True, "properties": {**common_props}, "additionalProperties": module_file_configuration, } @@ -72,7 +72,7 @@ lmod_configuration = { "type": "object", "default": {}, - "validate_spec": True, + "additionalKeysAreSpecs": True, "properties": { **common_props, "core_compilers": array_of_strings, @@ -80,7 +80,7 @@ "core_specs": array_of_strings, "filter_hierarchy_specs": { "type": "object", - "validate_spec": True, + "additionalKeysAreSpecs": True, "additionalProperties": array_of_strings, }, }, diff --git a/lib/spack/spack/test/schema.py b/lib/spack/spack/test/schema.py index d712576cc71..cb31ae8b64a 100644 --- a/lib/spack/spack/test/schema.py +++ b/lib/spack/spack/test/schema.py @@ -17,7 +17,7 @@ def validate_spec_schema(): return { "type": "object", - "validate_spec": True, + "additionalKeysAreSpecs": True, "patternProperties": {r"\w[\w-]*": {"type": "string"}}, } @@ -34,7 +34,7 @@ def module_suffixes_schema(): "type": "object", "properties": { "suffixes": { - "validate_spec": True, + "additionalKeysAreSpecs": True, "patternProperties": {r"\w[\w-]*": {"type": "string"}}, } },