diff --git a/hed/errors/error_messages.py b/hed/errors/error_messages.py index 5cded3402..b5dfb6060 100644 --- a/hed/errors/error_messages.py +++ b/hed/errors/error_messages.py @@ -5,8 +5,7 @@ """ from hed.errors.error_reporter import hed_error, hed_tag_error -from hed.errors.error_types import ValidationErrors, SchemaErrors, \ - SidecarErrors, SchemaWarnings, ErrorSeverity, DefinitionErrors, OnsetErrors, ColumnErrors +from hed.errors.error_types import ValidationErrors, SidecarErrors, ErrorSeverity, DefinitionErrors, OnsetErrors, ColumnErrors @hed_tag_error(ValidationErrors.UNITS_INVALID) @@ -231,57 +230,6 @@ def val_warning_default_units_used(tag, default_unit): return f"No unit specified. Using '{default_unit}' as the default - '{tag}'" -@hed_error(SchemaErrors.HED_SCHEMA_DUPLICATE_NODE) -def schema_error_hed_duplicate_node(tag, duplicate_tag_list, section): - tag_join_delimiter = "\n\t" - return f"Duplicate term '{str(tag)}' used {len(duplicate_tag_list)} places in '{section}' section schema as:" + \ - f"{tag_join_delimiter}{tag_join_delimiter.join(duplicate_tag_list)}" - - -@hed_error(SchemaErrors.HED_SCHEMA_DUPLICATE_FROM_LIBRARY) -def schema_error_hed_duplicate_node(tag, duplicate_tag_list, section): - tag_join_delimiter = "\n\t" - return f"Duplicate term '{str(tag)}' was found in the library and in the standard schema in '{section}' section schema as:" + \ - f"{tag_join_delimiter}{tag_join_delimiter.join(duplicate_tag_list)}" - - -@hed_error(SchemaErrors.SCHEMA_ATTRIBUTE_INVALID) -def schema_error_unknown_attribute(attribute_name, source_tag): - return f"Attribute '{attribute_name}' used by '{source_tag}' was not defined in the schema, " \ - f"or was used outside of it's defined class." - - -@hed_error(SchemaWarnings.INVALID_CHARACTERS_IN_DESC, default_severity=ErrorSeverity.WARNING, - actual_code=SchemaWarnings.HED_SCHEMA_CHARACTER_INVALID) -def schema_warning_invalid_chars_desc(desc_string, tag_name, problem_char, char_index): - return f"Invalid character '{problem_char}' in desc for '{tag_name}' at position {char_index}. '{desc_string}" - - -@hed_error(SchemaWarnings.INVALID_CHARACTERS_IN_TAG, default_severity=ErrorSeverity.WARNING, - actual_code=SchemaWarnings.HED_SCHEMA_CHARACTER_INVALID) -def schema_warning_invalid_chars_tag(tag_name, problem_char, char_index): - return f"Invalid character '{problem_char}' in tag '{tag_name}' at position {char_index}." - - -@hed_error(SchemaWarnings.INVALID_CAPITALIZATION, default_severity=ErrorSeverity.WARNING) -def schema_warning_invalid_capitalization(tag_name, problem_char, char_index): - return "First character must be a capital letter or number. " + \ - f"Found character '{problem_char}' in tag '{tag_name}' at position {char_index}." - - -@hed_error(SchemaWarnings.NON_PLACEHOLDER_HAS_CLASS, default_severity=ErrorSeverity.WARNING) -def schema_warning_non_placeholder_class(tag_name, invalid_attribute_name): - return "Only placeholder nodes('#') can have a unit or value class." + \ - f"Found {invalid_attribute_name} on {tag_name}" - - -@hed_error(SchemaWarnings.INVALID_ATTRIBUTE, default_severity=ErrorSeverity.ERROR) -def schema_error_invalid_attribute(tag_name, invalid_attribute_name): - return f"'{invalid_attribute_name}' should not be present in a loaded schema, found on '{tag_name}'." \ - f"Something went very wrong." - - - @hed_error(SidecarErrors.BLANK_HED_STRING) def sidecar_error_blank_hed_string(): return "No HED string found for Value or Category column." diff --git a/hed/errors/error_reporter.py b/hed/errors/error_reporter.py index 6eebe38f3..596882e37 100644 --- a/hed/errors/error_reporter.py +++ b/hed/errors/error_reporter.py @@ -167,8 +167,11 @@ def wrapper(tag, *args, severity=default_severity, **kwargs): # Import after hed_error decorators are defined. from hed.errors import error_messages +from hed.errors import schema_error_messages + # Intentional to make sure tools don't think the import is unused error_messages.mark_as_used = True +schema_error_messages.mark_as_used = True class ErrorHandler: @@ -545,6 +548,8 @@ def _add_single_error_to_dict(items, root=None, issue_to_add=None): def _error_dict_to_string(print_dict, add_link=True, level=0): output = "" + if print_dict is None: + return output for context, value in print_dict.items(): if context == "children": for child in value: diff --git a/hed/errors/error_types.py b/hed/errors/error_types.py index 18418a4f2..1fa43c8b1 100644 --- a/hed/errors/error_types.py +++ b/hed/errors/error_types.py @@ -106,19 +106,32 @@ class SidecarErrors: class SchemaErrors: - HED_SCHEMA_DUPLICATE_NODE = 'HED_SCHEMA_DUPLICATE_NODE' + SCHEMA_DUPLICATE_NODE = 'SCHEMA_DUPLICATE_NODE' SCHEMA_ATTRIBUTE_INVALID = 'SCHEMA_ATTRIBUTE_INVALID' - HED_SCHEMA_DUPLICATE_FROM_LIBRARY = "SCHEMA_LIBRARY_INVALID" + SCHEMA_DUPLICATE_FROM_LIBRARY = "SCHEMA_LIBRARY_INVALID" class SchemaWarnings: - INVALID_CHARACTERS_IN_DESC = "INVALID_CHARACTERS_IN_DESC" - INVALID_CHARACTERS_IN_TAG = "INVALID_CHARACTERS_IN_TAG" + SCHEMA_INVALID_CHARACTERS_IN_DESC = "SCHEMA_INVALID_CHARACTERS_IN_DESC" + SCHEMA_INVALID_CHARACTERS_IN_TAG = "SCHEMA_INVALID_CHARACTERS_IN_TAG" + # The actual reported error for the above two - HED_SCHEMA_CHARACTER_INVALID = "HED_SCHEMA_CHARACTER_INVALID" - INVALID_CAPITALIZATION = 'invalidCaps' - NON_PLACEHOLDER_HAS_CLASS = 'NON_PLACEHOLDER_HAS_CLASS' - INVALID_ATTRIBUTE = "INVALID_ATTRIBUTE" + SCHEMA_CHARACTER_INVALID = "SCHEMA_CHARACTER_INVALID" + SCHEMA_INVALID_CAPITALIZATION = 'invalidCaps' + SCHEMA_NON_PLACEHOLDER_HAS_CLASS = 'SCHEMA_NON_PLACEHOLDER_HAS_CLASS' + SCHEMA_INVALID_ATTRIBUTE = "SCHEMA_INVALID_ATTRIBUTE" + + +class SchemaAttributeErrors: + SCHEMA_DEPRECATED_INVALID = "SCHEMA_DEPRECATED_INVALID" + SCHEMA_SUGGESTED_TAG_INVALID = "SCHEMA_SUGGESTED_TAG_INVALID" + SCHEMA_RELATED_TAG_INVALID = "SCHEMA_RELATED_TAG_INVALID" + + SCHEMA_UNIT_CLASS_INVALID = "SCHEMA_UNIT_CLASS_INVALID" + SCHEMA_VALUE_CLASS_INVALID = "SCHEMA_VALUE_CLASS_INVALID" + + SCHEMA_DEFAULT_UNITS_INVALID = "SCHEMA_DEFAULT_UNITS_INVALID" + SCHEMA_CHILD_OF_DEPRECATED = "SCHEMA_CHILD_OF_DEPRECATED" # Reported as SCHEMA_DEPRECATED_INVALID class DefinitionErrors: diff --git a/hed/errors/schema_error_messages.py b/hed/errors/schema_error_messages.py new file mode 100644 index 000000000..b7fda9d50 --- /dev/null +++ b/hed/errors/schema_error_messages.py @@ -0,0 +1,84 @@ +from hed.errors.error_types import SchemaErrors, SchemaWarnings, ErrorSeverity, SchemaAttributeErrors +from hed.errors.error_reporter import hed_error + + +@hed_error(SchemaErrors.SCHEMA_DUPLICATE_NODE) +def schema_error_hed_duplicate_node(tag, duplicate_tag_list, section): + tag_join_delimiter = "\n\t" + return f"Duplicate term '{str(tag)}' used {len(duplicate_tag_list)} places in '{section}' section schema as:" + \ + f"{tag_join_delimiter}{tag_join_delimiter.join(duplicate_tag_list)}" + + +@hed_error(SchemaErrors.SCHEMA_DUPLICATE_FROM_LIBRARY) +def schema_error_hed_duplicate_from_library(tag, duplicate_tag_list, section): + tag_join_delimiter = "\n\t" + return f"Duplicate term '{str(tag)}' was found in the library and in the standard schema in '{section}' section schema as:" + \ + f"{tag_join_delimiter}{tag_join_delimiter.join(duplicate_tag_list)}" + + +@hed_error(SchemaErrors.SCHEMA_ATTRIBUTE_INVALID) +def schema_error_unknown_attribute(attribute_name, source_tag): + return f"Attribute '{attribute_name}' used by '{source_tag}' was not defined in the schema, " \ + f"or was used outside of it's defined class." + + +@hed_error(SchemaWarnings.SCHEMA_INVALID_CHARACTERS_IN_DESC, default_severity=ErrorSeverity.WARNING, + actual_code=SchemaWarnings.SCHEMA_CHARACTER_INVALID) +def schema_warning_invalid_chars_desc(desc_string, tag_name, problem_char, char_index): + return f"Invalid character '{problem_char}' in desc for '{tag_name}' at position {char_index}. '{desc_string}" + + +@hed_error(SchemaWarnings.SCHEMA_INVALID_CHARACTERS_IN_TAG, default_severity=ErrorSeverity.WARNING, + actual_code=SchemaWarnings.SCHEMA_CHARACTER_INVALID) +def schema_warning_invalid_chars_tag(tag_name, problem_char, char_index): + return f"Invalid character '{problem_char}' in tag '{tag_name}' at position {char_index}." + + +@hed_error(SchemaWarnings.SCHEMA_INVALID_CAPITALIZATION, default_severity=ErrorSeverity.WARNING) +def schema_warning_SCHEMA_INVALID_CAPITALIZATION(tag_name, problem_char, char_index): + return "First character must be a capital letter or number. " + \ + f"Found character '{problem_char}' in tag '{tag_name}' at position {char_index}." + + +@hed_error(SchemaWarnings.SCHEMA_NON_PLACEHOLDER_HAS_CLASS, default_severity=ErrorSeverity.WARNING) +def schema_warning_non_placeholder_class(tag_name, invalid_attribute_name): + return "Only placeholder nodes('#') can have a unit class, value class, or takes value." + \ + f"Found {invalid_attribute_name} on {tag_name}" + + +@hed_error(SchemaWarnings.SCHEMA_INVALID_ATTRIBUTE, default_severity=ErrorSeverity.ERROR) +def schema_error_SCHEMA_INVALID_ATTRIBUTE(tag_name, invalid_attribute_name): + return f"'{invalid_attribute_name}' should not be present in a loaded schema, found on '{tag_name}'." \ + f"Something went very wrong." + + +@hed_error(SchemaAttributeErrors.SCHEMA_DEPRECATED_INVALID) +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_DEPRECATED_INVALID) +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_SUGGESTED_TAG_INVALID) +def schema_error_SCHEMA_SUGGESTED_TAG_INVALID(suggestedTag, invalidSuggestedTag, attribute_name): + return f"Tag '{suggestedTag}' has an invalid {attribute_name}: '{invalidSuggestedTag}'." + + +@hed_error(SchemaAttributeErrors.SCHEMA_UNIT_CLASS_INVALID) +def schema_error_SCHEMA_UNIT_CLASS_INVALID(tag, unit_class, attribute_name): + return f"Tag '{tag}' has an invalid {attribute_name}: '{unit_class}'." + + +@hed_error(SchemaAttributeErrors.SCHEMA_VALUE_CLASS_INVALID) +def schema_error_SCHEMA_VALUE_CLASS_INVALID(tag, unit_class, attribute_name): + return f"Tag '{tag}' has an invalid {attribute_name}: '{unit_class}'." + + +@hed_error(SchemaAttributeErrors.SCHEMA_DEFAULT_UNITS_INVALID) +def schema_error_SCHEMA_DEFAULT_UNITS_INVALID(tag, bad_unit, valid_units): + valid_units = ",".join(valid_units) + return f"Tag '{tag}' has an invalid defaultUnit '{bad_unit}'. Valid units are: '{valid_units}'." diff --git a/hed/models/hed_tag.py b/hed/models/hed_tag.py index 56bb20d9b..9d470002b 100644 --- a/hed/models/hed_tag.py +++ b/hed/models/hed_tag.py @@ -270,7 +270,7 @@ def expanded(self): @property def expandable(self): - """Returns if this is expandable + """Returns what this expands to This is primarily used for Def/Def-expand tags at present. diff --git a/hed/schema/hed_cache.py b/hed/schema/hed_cache.py index 793cd6d85..299af6f66 100644 --- a/hed/schema/hed_cache.py +++ b/hed/schema/hed_cache.py @@ -60,13 +60,14 @@ def get_cache_directory(): return HED_CACHE_DIRECTORY -def get_hed_versions(local_hed_directory=None, library_name=None, get_libraries=False): +def get_hed_versions(local_hed_directory=None, library_name=None): """ Get the HED versions in the hed directory. Parameters: local_hed_directory (str): Directory to check for versions which defaults to hed_cache. library_name (str or None): An optional schema library name. - get_libraries (bool): If true, return a dictionary of version numbers, with an entry for each library name. + None retrieves the standard schema only. + Pass "all" to retrieve all standard and library schemas as a dict. Returns: list or dict: List of version numbers or dictionary {library_name: [versions]}. @@ -89,18 +90,16 @@ def get_hed_versions(local_hed_directory=None, library_name=None, get_libraries= if expression_match is not None: version = expression_match.group(3) found_library_name = expression_match.group(2) - if not get_libraries and found_library_name != library_name: + if library_name != "all" and found_library_name != library_name: continue if found_library_name not in all_hed_versions: all_hed_versions[found_library_name] = [] all_hed_versions[found_library_name].append(version) for name, hed_versions in all_hed_versions.items(): all_hed_versions[name] = _sort_version_list(hed_versions) - if get_libraries: - return all_hed_versions if library_name in all_hed_versions: return all_hed_versions[library_name] - return [] + return all_hed_versions def cache_specific_url(hed_xml_url, xml_version=None, library_name=None, cache_folder=None): diff --git a/hed/schema/hed_schema_constants.py b/hed/schema/hed_schema_constants.py index d747c2148..0cecc4ab6 100644 --- a/hed/schema/hed_schema_constants.py +++ b/hed/schema/hed_schema_constants.py @@ -41,6 +41,7 @@ class HedKey: RelatedTag = "relatedTag" SuggestedTag = "suggestedTag" Rooted = "rooted" + DeprecatedFrom = "deprecatedFrom" # All known properties BoolProperty = 'boolProperty' diff --git a/hed/schema/schema_attribute_validators.py b/hed/schema/schema_attribute_validators.py index 47dc7410b..d1d7f5ecb 100644 --- a/hed/schema/schema_attribute_validators.py +++ b/hed/schema/schema_attribute_validators.py @@ -9,9 +9,11 @@ bool """ -from hed.errors.error_types import SchemaWarnings, ValidationErrors +from hed.errors.error_types import SchemaWarnings, ValidationErrors, SchemaAttributeErrors from hed.errors.error_reporter import ErrorHandler from hed.schema.hed_schema import HedSchema +from hed.schema.hed_cache import get_hed_versions +from hed.schema.hed_schema_constants import HedKey def tag_is_placeholder_check(hed_schema, tag_entry, attribute_name): @@ -28,12 +30,13 @@ def tag_is_placeholder_check(hed_schema, tag_entry, attribute_name): """ issues = [] if not tag_entry.name.endswith("/#"): - issues += ErrorHandler.format_error(SchemaWarnings.NON_PLACEHOLDER_HAS_CLASS, tag_entry.name, + issues += ErrorHandler.format_error(SchemaWarnings.SCHEMA_NON_PLACEHOLDER_HAS_CLASS, tag_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. @@ -51,14 +54,55 @@ def tag_exists_check(hed_schema, tag_entry, attribute_name): split_tags = possible_tags.split(",") for org_tag in split_tags: if org_tag and org_tag not in hed_schema.tags: - issues += ErrorHandler.format_error(ValidationErrors.NO_VALID_TAG_FOUND, + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_SUGGESTED_TAG_INVALID, + tag_entry.name, org_tag, - index_in_tag=0, - index_in_tag_end=len(org_tag)) + attribute_name) return issues +def unit_class_exists(hed_schema, tag_entry, attribute_name): + issues = [] + 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: + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_UNIT_CLASS_INVALID, + tag_entry.name, + org_tag, + attribute_name) + + return issues + + +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: + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_VALUE_CLASS_INVALID, + 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: + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_DEFAULT_UNITS_INVALID, + tag_entry.name, + default_unit, + tag_entry.units) + + return issues + + +# This is effectively unused and can never fail - The schema would catch these errors and refuse to load def tag_exists_base_schema_check(hed_schema, tag_entry, attribute_name): """ Check if the single tag is a partnered schema tag @@ -78,4 +122,32 @@ def tag_exists_base_schema_check(hed_schema, tag_entry, attribute_name): index_in_tag=0, index_in_tag_end=len(rooted_tag)) + return issues + + +def tag_is_deprecated_check(hed_schema, tag_entry, attribute_name): + """ Check if the tag has a valid deprecatedFrom attribute, and that any children have it + + 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 = [] + deprecated_version = tag_entry.attributes.get(attribute_name, "") + library_name = tag_entry.has_attribute(HedKey.InLibrary, return_value=True) + all_versions = get_hed_versions(library_name=library_name) + if deprecated_version and deprecated_version not in all_versions: + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_DEPRECATED_INVALID, + tag_entry.name, + deprecated_version) + + for child in tag_entry.children.values(): + if not child.has_attribute(attribute_name): + issues += ErrorHandler.format_error(SchemaAttributeErrors.SCHEMA_CHILD_OF_DEPRECATED, + tag_entry.name, + child.name) return issues \ No newline at end of file diff --git a/hed/schema/schema_compliance.py b/hed/schema/schema_compliance.py index 59770e6b2..c75c11de3 100644 --- a/hed/schema/schema_compliance.py +++ b/hed/schema/schema_compliance.py @@ -1,6 +1,6 @@ """ Utilities for HED schema checking. """ -from hed.errors.error_types import ErrorContext, SchemaErrors, ErrorSeverity +from hed.errors.error_types import ErrorContext, SchemaErrors, ErrorSeverity, SchemaAttributeErrors, SchemaWarnings from hed.errors.error_reporter import ErrorHandler from hed.schema.hed_schema import HedSchema, HedKey from hed.schema import schema_attribute_validators @@ -45,12 +45,29 @@ def check_compliance(hed_schema, check_for_warnings=True, name=None, error_handl class SchemaValidator: """Validator class to wrap some code. In general, just call check_compliance.""" attribute_validators = { - HedKey.SuggestedTag: schema_attribute_validators.tag_exists_check, - HedKey.RelatedTag: schema_attribute_validators.tag_exists_check, - HedKey.UnitClass: schema_attribute_validators.tag_is_placeholder_check, - HedKey.ValueClass: schema_attribute_validators.tag_is_placeholder_check, - HedKey.Rooted: schema_attribute_validators.tag_exists_base_schema_check, + HedKey.SuggestedTag: [(schema_attribute_validators.tag_exists_check, + SchemaAttributeErrors.SCHEMA_SUGGESTED_TAG_INVALID)], + HedKey.RelatedTag: [(schema_attribute_validators.tag_exists_check, + SchemaAttributeErrors.SCHEMA_RELATED_TAG_INVALID)], + HedKey.UnitClass: [(schema_attribute_validators.tag_is_placeholder_check, + SchemaWarnings.SCHEMA_NON_PLACEHOLDER_HAS_CLASS), + (schema_attribute_validators.unit_class_exists, + SchemaAttributeErrors.SCHEMA_UNIT_CLASS_INVALID)], + HedKey.ValueClass: [(schema_attribute_validators.tag_is_placeholder_check, + SchemaWarnings.SCHEMA_NON_PLACEHOLDER_HAS_CLASS), + (schema_attribute_validators.value_class_exists, + SchemaAttributeErrors.SCHEMA_VALUE_CLASS_INVALID)], + # Rooted tag is implicitly verified on loading + # HedKey.Rooted: [(schema_attribute_validators.tag_exists_base_schema_check, + # SchemaAttributeErrors.SCHEMA_ROOTED_TAG_INVALID)], + HedKey.DeprecatedFrom: [(schema_attribute_validators.tag_is_deprecated_check, + SchemaAttributeErrors.SCHEMA_DEPRECATED_INVALID)], + HedKey.TakesValue: [(schema_attribute_validators.tag_is_placeholder_check, + SchemaWarnings.SCHEMA_NON_PLACEHOLDER_HAS_CLASS)], + HedKey.DefaultUnits: [(schema_attribute_validators.unit_exists, + SchemaAttributeErrors.SCHEMA_DEFAULT_UNITS_INVALID)] } + def __init__(self, hed_schema, check_for_warnings=True, error_handler=None): self.hed_schema = hed_schema self._check_for_warnings = check_for_warnings @@ -76,14 +93,16 @@ 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: - validator = self.attribute_validators.get(attribute_name) - if validator: - self.error_handler.push_error_context(ErrorContext.SCHEMA_ATTRIBUTE, attribute_name) - new_issues = validator(self.hed_schema, tag_entry, attribute_name) - for issue in new_issues: - issue['severity'] = ErrorSeverity.WARNING - self.error_handler.add_context_and_filter(new_issues) - issues_list += new_issues + validators = self.attribute_validators.get(attribute_name, None) + if validators: + for validator, error_code in validators: + self.error_handler.push_error_context(ErrorContext.SCHEMA_ATTRIBUTE, attribute_name) + new_issues = validator(self.hed_schema, tag_entry, attribute_name) + for issue in new_issues: + issue['code'] = error_code + issue['severity'] = ErrorSeverity.WARNING + self.error_handler.add_context_and_filter(new_issues) + issues_list += new_issues self.error_handler.pop_error_context() self.error_handler.pop_error_context() self.error_handler.pop_error_context() @@ -95,11 +114,12 @@ def check_duplicate_names(self): for section_key in self.hed_schema._sections: for name, duplicate_entries in self.hed_schema[section_key].duplicate_names.items(): values = set(entry.has_attribute(HedKey.InLibrary) for entry in duplicate_entries) - error_code = SchemaErrors.HED_SCHEMA_DUPLICATE_NODE + error_code = SchemaErrors.SCHEMA_DUPLICATE_NODE if len(values) == 2: - error_code = SchemaErrors.HED_SCHEMA_DUPLICATE_FROM_LIBRARY + error_code = SchemaErrors.SCHEMA_DUPLICATE_FROM_LIBRARY issues_list += self.error_handler.format_error_with_context(error_code, name, - duplicate_tag_list=[entry.name for entry in duplicate_entries], + duplicate_tag_list=[entry.name for entry in + duplicate_entries], section=section_key) return issues_list @@ -114,5 +134,3 @@ def check_invalid_chars(self): for tag_name, desc in self.hed_schema.get_desc_iter(): issues_list += validate_schema_description(tag_name, desc) return issues_list - - diff --git a/hed/schema/schema_validation_util.py b/hed/schema/schema_validation_util.py index 7805a3977..8404970e7 100644 --- a/hed/schema/schema_validation_util.py +++ b/hed/schema/schema_validation_util.py @@ -169,12 +169,12 @@ def validate_schema_term(hed_term): for i, char in enumerate(hed_term): if i == 0 and not (char.isdigit() or char.isupper()): - issues_list += ErrorHandler.format_error(SchemaWarnings.INVALID_CAPITALIZATION, + issues_list += ErrorHandler.format_error(SchemaWarnings.SCHEMA_INVALID_CAPITALIZATION, hed_term, char_index=i, problem_char=char) continue if char in ALLOWED_TAG_CHARS or char.isalnum(): continue - issues_list += ErrorHandler.format_error(SchemaWarnings.INVALID_CHARACTERS_IN_TAG, + issues_list += ErrorHandler.format_error(SchemaWarnings.SCHEMA_INVALID_CHARACTERS_IN_TAG, hed_term, char_index=i, problem_char=char) return issues_list @@ -199,6 +199,6 @@ def validate_schema_description(tag_name, hed_description): continue if char in ALLOWED_DESC_CHARS: continue - issues_list += ErrorHandler.format_error(SchemaWarnings.INVALID_CHARACTERS_IN_DESC, + issues_list += ErrorHandler.format_error(SchemaWarnings.SCHEMA_INVALID_CHARACTERS_IN_DESC, hed_description, tag_name, char_index=i, problem_char=char) return issues_list diff --git a/spec_tests/test_errors.py b/spec_tests/test_errors.py index 2ee73fc9e..ac817fa81 100644 --- a/spec_tests/test_errors.py +++ b/spec_tests/test_errors.py @@ -10,9 +10,6 @@ import json from hed import HedFileError from hed.errors import ErrorHandler, get_printable_issue_string -import shutil -from hed import schema -from hed.schema import hed_cache # To be removed eventually once all errors are being verified. @@ -48,7 +45,14 @@ "SIDECAR_BRACES_INVALID", "SCHEMA_LIBRARY_INVALID", - "SCHEMA_ATTRIBUTE_INVALID" + "SCHEMA_ATTRIBUTE_INVALID", + "SCHEMA_UNIT_CLASS_INVALID", + "SCHEMA_VALUE_CLASS_INVALID", + "SCHEMA_DEPRECATED_INVALID", + "SCHEMA_SUGGESTED_TAG_INVALID", + "SCHEMA_RELATED_TAG_INVALID", + "SCHEMA_NON_PLACEHOLDER_HAS_CLASS", + "SCHEMA_DEFAULT_UNITS_INVALID" ] skip_tests = { @@ -68,21 +72,9 @@ def setUpClass(cls): cls.fail_count = [] cls.default_sidecar = Sidecar(os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'test_sidecar.json'))) - # this is just to make sure 8.2.0 is in the cache(as you can't find it online yet) and could be cleaned up - cls.hed_cache_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'test_errors_cache/') - base_schema_dir = '../tests/data/schema_tests/merge_tests/' - cls.saved_cache_folder = hed_cache.HED_CACHE_DIRECTORY - schema.set_cache_directory(cls.hed_cache_dir) - cls.full_base_folder = os.path.join(os.path.dirname(os.path.realpath(__file__)), base_schema_dir) - cls.source_schema_path = os.path.join(cls.full_base_folder, "HED8.2.0.xml") - cls.cache_folder = hed_cache.get_cache_directory() - cls.schema_path_in_cache = os.path.join(cls.cache_folder, "HED8.2.0.xml") - shutil.copy(cls.source_schema_path, cls.schema_path_in_cache) - @classmethod def tearDownClass(cls): - shutil.rmtree(cls.hed_cache_dir) - schema.set_cache_directory(cls.saved_cache_folder) + pass def run_single_test(self, test_file): with open(test_file, "r") as fp: diff --git a/tests/data/schema_tests/merge_tests/issues_tests/overlapping_units.mediawiki b/tests/data/schema_tests/merge_tests/issues_tests/overlapping_units.mediawiki index 8f98fc80b..b7c4d5aa7 100644 --- a/tests/data/schema_tests/merge_tests/issues_tests/overlapping_units.mediawiki +++ b/tests/data/schema_tests/merge_tests/issues_tests/overlapping_units.mediawiki @@ -31,7 +31,7 @@ For more information see https://hed-schema-library.readthedocs.io/en/latest/ind !# end schema '''Unit classes''' -* weightUnitsNew {defaultUnits=testUnit} +* weightUnitsNew {defaultUnits=g} ** g {conversionFactor=100} diff --git a/tests/errors/test_error_reporter.py b/tests/errors/test_error_reporter.py index bec45f60e..c5f850aa9 100644 --- a/tests/errors/test_error_reporter.py +++ b/tests/errors/test_error_reporter.py @@ -69,7 +69,7 @@ def test_pop_error_context(self): def test_filter_issues_by_severity(self): error_list = self.error_handler.format_error_with_context(ValidationErrors.TAG_NOT_UNIQUE, "") - error_list += self.error_handler.format_error_with_context(SchemaWarnings.INVALID_CAPITALIZATION, + error_list += self.error_handler.format_error_with_context(SchemaWarnings.SCHEMA_INVALID_CAPITALIZATION, "dummy", problem_char="#", char_index=0) self.assertTrue(len(error_list) == 2) filtered_list = self.error_handler.filter_issues_by_severity(issues_list=error_list, @@ -79,7 +79,7 @@ def test_filter_issues_by_severity(self): def test_printable_issue_string(self): self.error_handler.push_error_context(ErrorContext.CUSTOM_TITLE, "Default Custom Title") error_list = self.error_handler.format_error_with_context(ValidationErrors.TAG_NOT_UNIQUE, "") - error_list += self.error_handler.format_error_with_context(SchemaWarnings.INVALID_CAPITALIZATION, + error_list += self.error_handler.format_error_with_context(SchemaWarnings.SCHEMA_INVALID_CAPITALIZATION, "dummy", problem_char="#", char_index=0) printable_issues = get_printable_issue_string(error_list) @@ -99,7 +99,7 @@ def test_printable_issue_string_with_filenames(self): self.error_handler.push_error_context(ErrorContext.CUSTOM_TITLE, "Default Custom Title") self.error_handler.push_error_context(ErrorContext.FILE_NAME, myfile) error_list = self.error_handler.format_error_with_context(ValidationErrors.TAG_NOT_UNIQUE, "") - error_list += self.error_handler.format_error_with_context(SchemaWarnings.INVALID_CAPITALIZATION, + error_list += self.error_handler.format_error_with_context(SchemaWarnings.SCHEMA_INVALID_CAPITALIZATION, "dummy", problem_char="#", char_index=0) printable_issues = get_printable_issue_string(error_list, skip_filename=False) diff --git a/tests/schema/test_hed_cache.py b/tests/schema/test_hed_cache.py index 55a343a26..3a33155bf 100644 --- a/tests/schema/test_hed_cache.py +++ b/tests/schema/test_hed_cache.py @@ -82,7 +82,7 @@ def test_cache_specific_url(self): self.assertTrue(local_filename) def test_get_hed_versions_all(self): - cached_versions = hed_cache.get_hed_versions(self.hed_cache_dir, get_libraries=True) + cached_versions = hed_cache.get_hed_versions(self.hed_cache_dir, library_name="all") self.assertIsInstance(cached_versions, dict) self.assertTrue(len(cached_versions) > 1) diff --git a/tests/schema/test_hed_schema.py b/tests/schema/test_hed_schema.py index f1b992511..9344df988 100644 --- a/tests/schema/test_hed_schema.py +++ b/tests/schema/test_hed_schema.py @@ -141,7 +141,7 @@ def test_short_tag_mapping(self): def test_schema_compliance(self): warnings = self.hed_schema_group.check_compliance(True) - self.assertEqual(len(warnings), 10) + self.assertEqual(len(warnings), 14) def test_bad_prefixes(self): schema = load_schema_version(xml_version="8.0.0") diff --git a/tests/schema/test_hed_schema_group.py b/tests/schema/test_hed_schema_group.py index 891dfc2b1..83e062ce6 100644 --- a/tests/schema/test_hed_schema_group.py +++ b/tests/schema/test_hed_schema_group.py @@ -15,7 +15,7 @@ def setUpClass(cls): def test_schema_compliance(self): warnings = self.hed_schema_group.check_compliance(True) - self.assertEqual(len(warnings), 10) + self.assertEqual(len(warnings), 14) def test_get_tag_entry(self): tag_entry = self.hed_schema_group.get_tag_entry("Event", schema_namespace="tl:") diff --git a/tests/schema/test_hed_schema_io.py b/tests/schema/test_hed_schema_io.py index 85f860b29..f3591ead3 100644 --- a/tests/schema/test_hed_schema_io.py +++ b/tests/schema/test_hed_schema_io.py @@ -287,7 +287,7 @@ def test_saving_merged2(self): os.remove(path2) def test_bad_schemas(self): - """These should all have one HED_SCHEMA_DUPLICATE_NODE issue""" + """These should all have one SCHEMA_DUPLICATE_NODE issue""" files = [ load_schema(os.path.join(self.full_base_folder, "issues_tests/overlapping_tags1.mediawiki")), load_schema(os.path.join(self.full_base_folder, "issues_tests/overlapping_tags2.mediawiki")), @@ -304,7 +304,7 @@ def test_bad_schemas(self): HedExceptions.SCHEMA_LIBRARY_INVALID, HedExceptions.SCHEMA_LIBRARY_INVALID, HedExceptions.SCHEMA_LIBRARY_INVALID, - SchemaErrors.HED_SCHEMA_DUPLICATE_NODE, + SchemaErrors.SCHEMA_DUPLICATE_NODE, ] for schema, expected_code in zip(files, expected_code): # print(schema.filename) diff --git a/tests/schema/test_schema_compliance.py b/tests/schema/test_schema_compliance.py index 49f6afe02..9a73248cb 100644 --- a/tests/schema/test_schema_compliance.py +++ b/tests/schema/test_schema_compliance.py @@ -17,3 +17,8 @@ def test_validate_schema(self): self.assertTrue(isinstance(issues, list)) self.assertTrue(len(issues) > 1) + def test_validate_schema_deprecated(self): + hed_schema = schema.load_schema_version("score_1.1.0") + issues = hed_schema.check_compliance() + self.assertTrue(isinstance(issues, list)) + self.assertTrue(len(issues) > 1) diff --git a/tests/schema/test_schema_converters.py b/tests/schema/test_schema_converters.py index 5f7c1d121..30cacaba6 100644 --- a/tests/schema/test_schema_converters.py +++ b/tests/schema/test_schema_converters.py @@ -79,7 +79,7 @@ class TestComplianceBase(unittest.TestCase): xml_file = '../data/schema_tests/HED8.0.0t.xml' wiki_file = '../data/schema_tests/HED8.0.0.mediawiki' can_compare = True - expected_issues = 5 + expected_issues = 7 @classmethod def setUpClass(cls): diff --git a/tests/schema/test_schema_validation_util.py b/tests/schema/test_schema_validation_util.py index 3c9494aac..7476a3733 100644 --- a/tests/schema/test_schema_validation_util.py +++ b/tests/schema/test_schema_validation_util.py @@ -28,13 +28,13 @@ def test_validate_schema_term(self): "@invalidcharatstart", ] expected_issues = [ - ErrorHandler.format_error(SchemaWarnings.INVALID_CAPITALIZATION, test_terms[0], char_index=0, + ErrorHandler.format_error(SchemaWarnings.SCHEMA_INVALID_CAPITALIZATION, test_terms[0], char_index=0, problem_char="i"), [], [], - ErrorHandler.format_error(SchemaWarnings.INVALID_CHARACTERS_IN_TAG, test_terms[3], char_index=11, + ErrorHandler.format_error(SchemaWarnings.SCHEMA_INVALID_CHARACTERS_IN_TAG, test_terms[3], char_index=11, problem_char="#"), - ErrorHandler.format_error(SchemaWarnings.INVALID_CAPITALIZATION, test_terms[4], char_index=0, + ErrorHandler.format_error(SchemaWarnings.SCHEMA_INVALID_CAPITALIZATION, test_terms[4], char_index=0, problem_char="@"), ] self.validate_term_base(test_terms, expected_issues) @@ -50,13 +50,13 @@ def test_validate_schema_description(self): [], [], [], - ErrorHandler.format_error(SchemaWarnings.INVALID_CHARACTERS_IN_DESC, test_descs[3], "dummy", + ErrorHandler.format_error(SchemaWarnings.SCHEMA_INVALID_CHARACTERS_IN_DESC, test_descs[3], "dummy", char_index=60, problem_char="@") - + ErrorHandler.format_error(SchemaWarnings.INVALID_CHARACTERS_IN_DESC, test_descs[3], "dummy", + + ErrorHandler.format_error(SchemaWarnings.SCHEMA_INVALID_CHARACTERS_IN_DESC, test_descs[3], "dummy", char_index=61, problem_char="$") - + ErrorHandler.format_error(SchemaWarnings.INVALID_CHARACTERS_IN_DESC, test_descs[3], "dummy", + + ErrorHandler.format_error(SchemaWarnings.SCHEMA_INVALID_CHARACTERS_IN_DESC, test_descs[3], "dummy", char_index=62, problem_char="%") - + ErrorHandler.format_error(SchemaWarnings.INVALID_CHARACTERS_IN_DESC, test_descs[3], "dummy", + + ErrorHandler.format_error(SchemaWarnings.SCHEMA_INVALID_CHARACTERS_IN_DESC, test_descs[3], "dummy", char_index=63, problem_char="*") ]