diff --git a/hed/errors/error_types.py b/hed/errors/error_types.py index 3fba4b1b..b7c9b38f 100644 --- a/hed/errors/error_types.py +++ b/hed/errors/error_types.py @@ -127,7 +127,10 @@ class SchemaWarnings: class SchemaAttributeErrors: SCHEMA_ATTRIBUTE_INVALID = 'SCHEMA_ATTRIBUTE_INVALID' SCHEMA_ATTRIBUTE_VALUE_INVALID = 'SCHEMA_ATTRIBUTE_VALUE_INVALID' + SCHEMA_DEPRECATION_ERROR = 'SCHEMA_DEPRECATION_ERROR' SCHEMA_DEPRECATED_INVALID = "SCHEMA_DEPRECATED_INVALID" + SCHEMA_CHILD_OF_DEPRECATED = "SCHEMA_CHILD_OF_DEPRECATED" + SCHEMA_ATTRIBUTE_VALUE_DEPRECATED = "SCHEMA_ATTRIBUTE_VALUE_DEPRECATED" SCHEMA_SUGGESTED_TAG_INVALID = "SCHEMA_SUGGESTED_TAG_INVALID" SCHEMA_UNIT_CLASS_INVALID = "SCHEMA_UNIT_CLASS_INVALID" @@ -136,7 +139,7 @@ class SchemaAttributeErrors: SCHEMA_IN_LIBRARY_INVALID = "SCHEMA_IN_LIBRARY_INVALID" SCHEMA_DEFAULT_UNITS_INVALID = "SCHEMA_DEFAULT_UNITS_INVALID" - SCHEMA_CHILD_OF_DEPRECATED = "SCHEMA_CHILD_OF_DEPRECATED" + SCHEMA_DEFAULT_UNITS_DEPRECATED = "SCHEMA_DEFAULT_UNITS_DEPRECATED" SCHEMA_CONVERSION_FACTOR_NOT_POSITIVE = "SCHEMA_CONVERSION_FACTOR_NOT_POSITIVE" diff --git a/hed/errors/schema_error_messages.py b/hed/errors/schema_error_messages.py index 1eb62b3c..7e2b269e 100644 --- a/hed/errors/schema_error_messages.py +++ b/hed/errors/schema_error_messages.py @@ -49,17 +49,24 @@ def schema_warning_non_placeholder_class(tag_name, invalid_attribute_name): @hed_error(SchemaAttributeErrors.SCHEMA_DEPRECATED_INVALID, - actual_code=SchemaAttributeErrors.SCHEMA_ATTRIBUTE_VALUE_INVALID) + actual_code=SchemaAttributeErrors.SCHEMA_DEPRECATION_ERROR) def schema_error_SCHEMA_DEPRECATED_INVALID(tag_name, invalid_deprecated_version): return f"'{tag_name}' has invalid or unknown value in attribute deprecatedFrom: '{invalid_deprecated_version}'." @hed_error(SchemaAttributeErrors.SCHEMA_CHILD_OF_DEPRECATED, - actual_code=SchemaAttributeErrors.SCHEMA_ATTRIBUTE_VALUE_INVALID) + actual_code=SchemaAttributeErrors.SCHEMA_DEPRECATION_ERROR) def schema_error_SCHEMA_CHILD_OF_DEPRECATED(deprecated_tag, non_deprecated_child): return f"Deprecated tag '{deprecated_tag}' has a child that is not deprecated: '{non_deprecated_child}'." +@hed_error(SchemaAttributeErrors.SCHEMA_ATTRIBUTE_VALUE_DEPRECATED, + actual_code=SchemaAttributeErrors.SCHEMA_DEPRECATION_ERROR) +def schema_error_SCHEMA_ATTRIBUTE_VALUE_DEPRECATED(tag, deprecated_suggestion, attribute_name): + return (f"Tag '{tag}' {attribute_name} uses '{deprecated_suggestion}' which has been deprecated " + f"and an alternative method of tagging should be used.") + + @hed_error(SchemaAttributeErrors.SCHEMA_SUGGESTED_TAG_INVALID, actual_code=SchemaAttributeErrors.SCHEMA_ATTRIBUTE_VALUE_INVALID) def schema_error_SCHEMA_SUGGESTED_TAG_INVALID(suggestedTag, invalidSuggestedTag, attribute_name): @@ -85,6 +92,12 @@ def schema_error_SCHEMA_DEFAULT_UNITS_INVALID(tag, bad_unit, valid_units): return f"Tag '{tag}' has an invalid defaultUnit '{bad_unit}'. Valid units are: '{valid_units}'." +@hed_error(SchemaAttributeErrors.SCHEMA_DEFAULT_UNITS_DEPRECATED, + actual_code=SchemaAttributeErrors.SCHEMA_DEPRECATION_ERROR) +def schema_error_SCHEMA_DEFAULT_UNITS_DEPRECATED(unit_class, bad_unit): + return f"Unit class '{unit_class}' defaultUnit '{bad_unit}' is deprecated. Please find an alternative default." + + @hed_error(SchemaAttributeErrors.SCHEMA_CONVERSION_FACTOR_NOT_POSITIVE, actual_code=SchemaAttributeErrors.SCHEMA_ATTRIBUTE_VALUE_INVALID) def schema_error_SCHEMA_CONVERSION_FACTOR_NOT_POSITIVE(tag, conversion_factor): diff --git a/hed/schema/schema_attribute_validators.py b/hed/schema/schema_attribute_validators.py index cea68fa9..4dd39d02 100644 --- a/hed/schema/schema_attribute_validators.py +++ b/hed/schema/schema_attribute_validators.py @@ -14,7 +14,7 @@ from hed.errors.error_types import SchemaWarnings, ValidationErrors, SchemaAttributeErrors from hed.errors.error_reporter import ErrorHandler from hed.schema.hed_cache import get_hed_versions -from hed.schema.hed_schema_constants import HedKey, character_types +from hed.schema.hed_schema_constants import HedKey, character_types, HedSectionKey from hed.schema.schema_validation_util import schema_version_for_library from semantic_version import Version @@ -39,6 +39,36 @@ def tag_is_placeholder_check(hed_schema, tag_entry, attribute_name): return issues +def attribute_is_deprecated(hed_schema, tag_entry, attribute_name): + """ Check if the attribute is deprecated. + + does not check value. + + Parameters: + hed_schema (HedSchema): The schema to use for validation + tag_entry (HedSchemaEntry): The schema entry for this tag. + attribute_name (str): The name of this attribute + + Returns: + list: A list of issues. Each issue is a dictionary. + """ + issues = [] + # Attributes has to check properties + section_key = HedSectionKey.Attributes + if tag_entry.section_key == HedSectionKey.Attributes: + section_key = HedSectionKey.Properties + + attribute_entry = hed_schema.get_tag_entry(attribute_name, section_key) + if (attribute_entry and attribute_entry.has_attribute(HedKey.DeprecatedFrom) + and not tag_entry.has_attribute(HedKey.DeprecatedFrom)): + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_ATTRIBUTE_VALUE_DEPRECATED, + tag_entry.name, + attribute_entry.name, + attribute_name) + + return issues + + # todo: This needs to be refactored, these next several functions are near identical def tag_exists_check(hed_schema, tag_entry, attribute_name): """ Check if the list of possible tags exists in the schema. @@ -56,11 +86,18 @@ def tag_exists_check(hed_schema, tag_entry, attribute_name): possible_tags = tag_entry.attributes.get(attribute_name, "") split_tags = possible_tags.split(",") for org_tag in split_tags: - if org_tag and org_tag not in hed_schema.tags: + org_entry = hed_schema.tags.get(org_tag) + if org_tag and not org_entry: issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_SUGGESTED_TAG_INVALID, tag_entry.name, org_tag, attribute_name) + elif (org_entry and org_entry.has_attribute(HedKey.DeprecatedFrom) + and not tag_entry.has_attribute(HedKey.DeprecatedFrom)): + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_ATTRIBUTE_VALUE_DEPRECATED, + tag_entry.name, + org_tag, + attribute_name) return issues @@ -70,11 +107,18 @@ def unit_class_exists(hed_schema, tag_entry, attribute_name): possible_unit_classes = tag_entry.attributes.get(attribute_name, "") split_tags = possible_unit_classes.split(",") for org_tag in split_tags: - if org_tag and org_tag not in hed_schema.unit_classes: + unit_class_entry = hed_schema.unit_classes.get(org_tag) + if org_tag and not unit_class_entry: issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_UNIT_CLASS_INVALID, tag_entry.name, org_tag, attribute_name) + elif (unit_class_entry and unit_class_entry.has_attribute(HedKey.DeprecatedFrom) + and not tag_entry.has_attribute(HedKey.DeprecatedFrom)): + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_ATTRIBUTE_VALUE_DEPRECATED, + tag_entry.name, + org_tag, + attribute_name) return issues @@ -83,24 +127,38 @@ def value_class_exists(hed_schema, tag_entry, attribute_name): issues = [] possible_value_classes = tag_entry.attributes.get(attribute_name, "") split_tags = possible_value_classes.split(",") + for org_tag in split_tags: - if org_tag and org_tag not in hed_schema.value_classes: + value_class_entry = hed_schema.value_classes.get(org_tag) + if org_tag and not value_class_entry: issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_VALUE_CLASS_INVALID, tag_entry.name, org_tag, attribute_name) + elif (value_class_entry and value_class_entry.has_attribute(HedKey.DeprecatedFrom) + and not tag_entry.has_attribute(HedKey.DeprecatedFrom)): + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_ATTRIBUTE_VALUE_DEPRECATED, + tag_entry.name, + org_tag, + attribute_name) return issues def unit_exists(hed_schema, tag_entry, attribute_name): issues = [] - default_unit = tag_entry.attributes.get(attribute_name, "") - if default_unit and default_unit not in tag_entry.derivative_units: + unit = tag_entry.attributes.get(attribute_name, "") + unit_entry = tag_entry.derivative_units.get(unit) + if unit and not unit_entry: issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_DEFAULT_UNITS_INVALID, tag_entry.name, - default_unit, + unit, tag_entry.units) + elif (unit_entry and unit_entry.has_attribute(HedKey.DeprecatedFrom) + and not tag_entry.has_attribute(HedKey.DeprecatedFrom)): + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_DEFAULT_UNITS_DEPRECATED, + tag_entry.name, + unit_entry.name) return issues diff --git a/hed/schema/schema_compliance.py b/hed/schema/schema_compliance.py index d5ca8d03..f3afbaac 100644 --- a/hed/schema/schema_compliance.py +++ b/hed/schema/schema_compliance.py @@ -86,7 +86,9 @@ def check_attributes(self): for tag_entry in self.hed_schema[section_key].values(): self.error_handler.push_error_context(ErrorContext.SCHEMA_TAG, tag_entry.name) for attribute_name in tag_entry.attributes: - validators = self.attribute_validators.get(attribute_name, []) + # Always check deprecated + validators = self.attribute_validators.get(attribute_name, []) \ + + [schema_attribute_validators.attribute_is_deprecated] for validator in validators: self.error_handler.push_error_context(ErrorContext.SCHEMA_ATTRIBUTE, attribute_name) new_issues = validator(self.hed_schema, tag_entry, attribute_name)