diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 917e9d69..ac17f364 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -11,8 +11,9 @@ on: jobs: claude-review: # Avoid duplicate runs: use pull_request for same-repo, pull_request_target for forks - # Skip bot PRs (dependabot, renovate, etc.) - they don't need code review + # Skip bot PRs (dependabot, renovate, etc.) and draft PRs if: | + !github.event.pull_request.draft && github.event.pull_request.user.type != 'Bot' && !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) && !(github.event_name == 'pull_request_target' && !github.event.pull_request.head.repo.fork) @@ -28,7 +29,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 with: - ref: ${{ github.event.pull_request.head.sha }} + # Use base.sha, not head.sha: checking out fork code under + # pull_request_target would give untrusted code access to secrets. + # Claude reads changes via gh pr diff (API), not the local checkout. + ref: ${{ github.event.pull_request.base.sha }} fetch-depth: 1 - name: Run Claude Code Review diff --git a/hed/__init__.py b/hed/__init__.py index 430800b7..5bd398c5 100644 --- a/hed/__init__.py +++ b/hed/__init__.py @@ -1,9 +1,8 @@ from hed.models.hed_string import HedString from hed.models.hed_tag import HedTag +from hed.models.hed_group import HedGroup from hed.errors.error_reporter import get_printable_issue_string from hed.errors.exceptions import HedFileError, HedExceptions, HedQueryError - -from hed.models.base_input import BaseInput from hed.models.spreadsheet_input import SpreadsheetInput from hed.models.tabular_input import TabularInput from hed.models.sidecar import Sidecar diff --git a/hed/errors/__init__.py b/hed/errors/__init__.py index a79bc7f7..5657aed4 100644 --- a/hed/errors/__init__.py +++ b/hed/errors/__init__.py @@ -4,6 +4,8 @@ ErrorHandler, separate_issues, get_printable_issue_string, + get_printable_issue_string_html, + check_for_any_errors, sort_issues, iter_errors, ) @@ -12,9 +14,11 @@ TemporalErrors, SchemaErrors, SchemaWarnings, + SchemaAttributeErrors, SidecarErrors, ValidationErrors, ColumnErrors, + TagQualityErrors, ) from .error_types import ErrorContext, ErrorSeverity -from .exceptions import HedExceptions, HedFileError +from .exceptions import HedExceptions, HedFileError, HedQueryError diff --git a/hed/errors/error_types.py b/hed/errors/error_types.py index 17452d39..2384455f 100644 --- a/hed/errors/error_types.py +++ b/hed/errors/error_types.py @@ -1,8 +1,10 @@ """Error codes used in different error messages.""" +from enum import IntEnum -class ErrorSeverity: - """Severity codes for errors""" + +class ErrorSeverity(IntEnum): + """Severity codes for errors.""" ERROR = 1 WARNING = 10 @@ -95,7 +97,7 @@ class ValidationErrors: INVALID_VALUE_CLASS_CHARACTER = "INVALID_VALUE_CLASS_CHARACTER" INVALID_VALUE_CLASS_VALUE = "INVALID_VALUE_CLASS_VALUE" - INVALID_TAG_CHARACTER = "invalidTagCharacter" + INVALID_TAG_CHARACTER = "INVALID_TAG_CHARACTER" HED_PLACEHOLDER_OUT_OF_CONTEXT = "HED_PLACEHOLDER_OUT_OF_CONTEXT" CURLY_BRACE_UNSUPPORTED_HERE = "CURLY_BRACE_UNSUPPORTED_HERE" @@ -105,11 +107,11 @@ class ValidationErrors: class SidecarErrors: # These are for json sidecar validation errors(sidecars can also produce most normal validation errors) - BLANK_HED_STRING = "blankValueString" - WRONG_HED_DATA_TYPE = "wrongHedDataType" - INVALID_POUND_SIGNS_VALUE = "invalidNumberPoundSigns" - INVALID_POUND_SIGNS_CATEGORY = "tooManyPoundSigns" - UNKNOWN_COLUMN_TYPE = "sidecarUnknownColumn" + BLANK_HED_STRING = "BLANK_HED_STRING" + WRONG_HED_DATA_TYPE = "WRONG_HED_DATA_TYPE" + INVALID_POUND_SIGNS_VALUE = "INVALID_POUND_SIGNS_VALUE" + INVALID_POUND_SIGNS_CATEGORY = "INVALID_POUND_SIGNS_CATEGORY" + UNKNOWN_COLUMN_TYPE = "UNKNOWN_COLUMN_TYPE" SIDECAR_HED_USED = "SIDECAR_HED_USED" SIDECAR_NA_USED = "SIDECAR_NA_USED" SIDECAR_BRACES_INVALID = "SIDECAR_BRACES_INVALID" @@ -128,7 +130,7 @@ class SchemaWarnings: # The actual reported error for the above two SCHEMA_CHARACTER_INVALID = "SCHEMA_CHARACTER_INVALID" - SCHEMA_INVALID_CAPITALIZATION = "invalidCaps" + SCHEMA_INVALID_CAPITALIZATION = "SCHEMA_INVALID_CAPITALIZATION" SCHEMA_NON_PLACEHOLDER_HAS_CLASS = "SCHEMA_NON_PLACEHOLDER_HAS_CLASS" SCHEMA_PROLOGUE_CHARACTER_INVALID = "SCHEMA_PROLOGUE_CHARACTER_INVALID" @@ -165,9 +167,9 @@ class SchemaAttributeErrors: class DefinitionErrors: # These are all DEFINITION_INVALID errors - WRONG_NUMBER_PLACEHOLDER_TAGS = "wrongNumberPlaceholderTags" - DUPLICATE_DEFINITION = "duplicateDefinition" - INVALID_DEFINITION_EXTENSION = "invalidDefExtension" + WRONG_NUMBER_PLACEHOLDER_TAGS = "WRONG_NUMBER_PLACEHOLDER_TAGS" + DUPLICATE_DEFINITION = "DUPLICATE_DEFINITION" + INVALID_DEFINITION_EXTENSION = "INVALID_DEFINITION_EXTENSION" DEF_TAG_IN_DEFINITION = "DEF_TAG_IN_DEFINITION" NO_DEFINITION_CONTENTS = "NO_DEFINITION_CONTENTS" PLACEHOLDER_NO_TAKES_VALUE = "PLACEHOLDER_NO_TAKES_VALUE" diff --git a/hed/errors/exceptions.py b/hed/errors/exceptions.py index d2f7d71e..cf0ae543 100644 --- a/hed/errors/exceptions.py +++ b/hed/errors/exceptions.py @@ -7,11 +7,11 @@ class HedExceptions: GENERIC_ERROR = "GENERIC_ERROR" # A list of all exceptions that can be generated by the hedtools. URL_ERROR = "URL_ERROR" - FILE_NOT_FOUND = "fileNotFound" - BAD_PARAMETERS = "badParameters" - CANNOT_PARSE_XML = "cannotParseXML" - CANNOT_PARSE_JSON = "cannotParseJson" - INVALID_EXTENSION = "invalidExtension" + FILE_NOT_FOUND = "FILE_NOT_FOUND" + BAD_PARAMETERS = "BAD_PARAMETERS" + CANNOT_PARSE_XML = "CANNOT_PARSE_XML" + CANNOT_PARSE_JSON = "CANNOT_PARSE_JSON" + INVALID_EXTENSION = "INVALID_EXTENSION" INVALID_HED_FORMAT = "INVALID_HED_FORMAT" INVALID_DATAFRAME = "INVALID_DATAFRAME" @@ -24,7 +24,7 @@ class HedExceptions: SCHEMA_SECTION_MISSING = "SCHEMA_SECTION_MISSING" SCHEMA_INVALID = "SCHEMA_INVALID" - WIKI_SEPARATOR_INVALID = "invalidSectionSeparator" + WIKI_SEPARATOR_INVALID = "WIKI_SEPARATOR_INVALID" # This issue will contain a list of lines with issues. WIKI_DELIMITERS_INVALID = "WIKI_DELIMITERS_INVALID" diff --git a/hed/models/__init__.py b/hed/models/__init__.py index f2dea62d..2d424f17 100644 --- a/hed/models/__init__.py +++ b/hed/models/__init__.py @@ -48,10 +48,9 @@ """ from .base_input import BaseInput -from .column_mapper import ColumnMapper from .column_metadata import ColumnMetadata, ColumnType from .definition_dict import DefinitionDict -from .definition_entry import DefinitionEntry +from .model_constants import DefTagNames, TopTagReturnType from .query_handler import QueryHandler from .query_service import get_query_handlers, search_hed_objs from .hed_group import HedGroup diff --git a/hed/models/column_mapper.py b/hed/models/column_mapper.py index dc8adb00..94b19b97 100644 --- a/hed/models/column_mapper.py +++ b/hed/models/column_mapper.py @@ -16,10 +16,28 @@ class ColumnMapper: - """Mapping of a base input file columns into HED tags. + """Translates tabular file columns into HED tag streams for validation and analysis. + + ``ColumnMapper`` is the low-level engine behind :class:`~hed.models.TabularInput` and + :class:`~hed.models.SpreadsheetInput`. It resolves column definitions from a + :class:`~hed.models.Sidecar` and/or explicit parameters into a per-column transform + pipeline that produces HED strings row-by-row. + + **Use this class directly when you need to:** + + - Build a custom tabular reader that doesn't subclass :class:`~hed.models.BaseInput`. + - Inspect or override column mappings before validating (e.g. dynamic column + selection at runtime). + - Reuse a single mapper across many DataFrames for performance. + + **For the common case** (reading a BIDS events file), prefer + :class:`~hed.models.TabularInput` which wraps ``ColumnMapper`` automatically. Notes: - - All column numbers are 0 based. + - All column numbers are 0-based. + - The ``column_prefix_dictionary`` parameter is treated as a shorthand for + creating value columns: ``{"col": "Description"}`` becomes + ``{"col": "Description/#"}`` internally. """ def __init__( diff --git a/hed/models/definition_entry.py b/hed/models/definition_entry.py index 04941d00..6002eaf4 100644 --- a/hed/models/definition_entry.py +++ b/hed/models/definition_entry.py @@ -7,7 +7,29 @@ class DefinitionEntry: - """A single definition.""" + """Stores the resolved contents of a single HED Definition. + + A ``DefinitionEntry`` is created when a ``Definition/`` tag group is parsed + and stored in a :class:`~hed.models.DefinitionDict`. It captures: + + - **name** — the lower-cased label portion (without ``Definition/``). + - **contents** — the inner :class:`~hed.models.HedGroup` of the definition + (``None`` if the definition body is empty). + - **takes_value** — whether exactly one tag inside contains a ``#`` placeholder + (i.e. the definition expects a run-time value via ``Def/name/value``). + - **source_context** — the error-context stack captured at parse time, used + to produce precise error messages when the definition is later expanded. + + **Use this class directly when you need to:** + + - Iterate over a :class:`~hed.models.DefinitionDict` and inspect individual + definition bodies or their placeholder status. + - Build tooling that expands, serialises, or analyses HED definitions + programmatically. + + **Most users never need this class** — :meth:`~hed.models.DefinitionDict.get_def_entry` + and :meth:`~hed.models.DefinitionDict.expand_def_tag` handle the common workflows. + """ def __init__(self, name, contents, takes_value, source_context): """Initialize info for a single definition. diff --git a/hed/models/hed_string.py b/hed/models/hed_string.py index 4cdd4577..13cdeccf 100644 --- a/hed/models/hed_string.py +++ b/hed/models/hed_string.py @@ -5,7 +5,7 @@ import copy from hed.models.hed_group import HedGroup from hed.models.hed_tag import HedTag -from hed.models.model_constants import DefTagNames +from hed.models.model_constants import DefTagNames, TopTagReturnType class HedString(HedGroup): @@ -128,7 +128,9 @@ def remove_definitions(self): This does not validate definitions and will blindly removing invalid ones as well. """ - definition_groups = self.find_top_level_tags({DefTagNames.DEFINITION_KEY}, include_groups=1) + definition_groups = self.find_top_level_tags( + {DefTagNames.DEFINITION_KEY}, include_groups=TopTagReturnType.GROUPS + ) if definition_groups: self.remove(definition_groups) @@ -362,10 +364,11 @@ def find_top_level_tags(self, anchor_tags, include_groups=2) -> list: Parameters: anchor_tags (container): A list/set/etc. of short_base_tags to find groups by. - include_groups (0, 1 or 2): Parameter indicating what return values to include. - If 0: return only tags. - If 1: return only groups. - If 2 or any other value: return both. + include_groups (TopTagReturnType or int): Controls what is returned. + Use :class:`~hed.models.TopTagReturnType` constants for clarity. + ``TAGS`` (0): return only anchor tags. + ``GROUPS`` (1): return only groups. + ``BOTH`` (2, default): return ``(tag, group)`` pairs. Returns: list: The returned result depends on include_groups. diff --git a/hed/models/model_constants.py b/hed/models/model_constants.py index be5a37b8..873a93ab 100644 --- a/hed/models/model_constants.py +++ b/hed/models/model_constants.py @@ -1,5 +1,21 @@ """Defined constants for definitions, def labels, and expanded labels.""" +from enum import IntEnum + + +class TopTagReturnType(IntEnum): + """Return-type selector for :meth:`~hed.models.HedString.find_top_level_tags`. + + Attributes: + TAGS: Return only the anchor :class:`~hed.models.HedTag` objects. + GROUPS: Return only the top-level :class:`~hed.models.HedGroup` objects. + BOTH: Return ``(tag, group)`` pairs (default). + """ + + TAGS = 0 + GROUPS = 1 + BOTH = 2 + class DefTagNames: """Source names for definitions, def labels, and expanded labels.""" diff --git a/hed/schema/__init__.py b/hed/schema/__init__.py index f381bd6b..f3c87ace 100644 --- a/hed/schema/__init__.py +++ b/hed/schema/__init__.py @@ -41,9 +41,7 @@ """ from .hed_schema import HedSchema -from .hed_schema_entry import HedSchemaEntry, UnitClassEntry, UnitEntry, HedTagEntry from .hed_schema_group import HedSchemaGroup -from .hed_schema_section import HedSchemaSection from .hed_schema_io import load_schema, load_schema_version, from_string, get_hed_xml_version, from_dataframes from .hed_schema_constants import HedKey, HedSectionKey from .hed_cache import cache_xml_versions, get_hed_versions, set_cache_directory, get_cache_directory diff --git a/hed/schema/hed_schema_entry.py b/hed/schema/hed_schema_entry.py index 1c2b4497..bcf43b48 100644 --- a/hed/schema/hed_schema_entry.py +++ b/hed/schema/hed_schema_entry.py @@ -10,10 +10,30 @@ class HedSchemaEntry: - """A single node in a HedSchema. + """A single node in the HED schema vocabulary. - The structure contains all the node information including attributes and properties. + Every term, unit, unit class, value class, attribute, and property that + appears in a loaded :class:`~hed.schema.HedSchema` is represented as a + ``HedSchemaEntry`` (or one of its subclasses). The entry stores the node's + name, all declared attributes (e.g. ``takesValue``, ``allowedCharacter``), + its description, and a back-reference to its containing + :class:`~hed.schema.HedSchemaSection`. + Concrete subclasses add section-specific state: + + - :class:`HedTagEntry` — vocabulary tag nodes. + - :class:`UnitClassEntry` — unit class nodes (e.g. *time*, *mass*). + - :class:`UnitEntry` — individual unit nodes (e.g. *second*, *gram*). + + **Use this class (or its subclasses) directly when you need to:** + + - Introspect schema vocabulary (e.g. list all tags with ``takesValue``). + - Build schema validators, schema browsers, or schema-diff tools. + - Implement custom HED annotation tooling that looks up tag metadata. + + **Most users never need this class** — :meth:`~hed.schema.HedSchema.get_tag_entry` + and :meth:`~hed.schema.HedSchema.get_all_schema_tags` are sufficient for the + common lookup patterns. """ def __init__(self, name, section): @@ -142,7 +162,25 @@ def _compare_attributes_no_order(left, right): class UnitClassEntry(HedSchemaEntry): - """A single unit class entry in the HedSchema.""" + """A unit class node in the HED schema (e.g. *time*, *mass*, *frequency*). + + Extends :class:`HedSchemaEntry` with the set of :class:`UnitEntry` objects + that belong to the class and a pre-computed ``derivative_units`` dict that + maps every accepted surface form (including SI prefixes and plurals) to its + canonical :class:`UnitEntry`. + + Typical access pattern:: + + unit_class = schema.get_tag_entry("time", HedSectionKey.UnitClasses) + for name, unit in unit_class.units.items(): + print(name, unit.attributes) + + Attributes: + units (dict[str, UnitEntry]): Map from unit name to entry after + :meth:`finalize_entry` is called. + derivative_units (dict[str, UnitEntry]): Map from every accepted + surface form (plural, SI-prefixed, etc.) to the base unit entry. + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -216,7 +254,19 @@ def get_derivative_unit_entry(self, units): class UnitEntry(HedSchemaEntry): - """A single unit entry with modifiers in the HedSchema.""" + """A single unit node in the HED schema (e.g. *second*, *gram*, *hertz*). + + Extends :class:`HedSchemaEntry` with the list of SI unit modifiers that + apply to this unit, a pre-computed ``derivative_units`` mapping (surface + form → conversion factor), and a back-reference to the parent + :class:`UnitClassEntry`. + + Attributes: + unit_modifiers (list[HedSchemaEntry]): SI modifier entries (e.g. *milli*, *kilo*). + derivative_units (dict[str, float]): Map from every accepted surface + form to its numeric conversion factor relative to the SI base unit. + unit_class_entry (UnitClassEntry): The parent unit class. + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -270,7 +320,26 @@ def get_conversion_factor(self, unit_name): class HedTagEntry(HedSchemaEntry): - """A single tag entry in the HedSchema.""" + """A vocabulary tag node in the HED schema. + + Extends :class:`HedSchemaEntry` with full/short tag name forms, value-class + and unit-class associations, and helper methods for tag-path traversal. + + Typical access pattern:: + + entry = schema.get_tag_entry("Sensory-event") + print(entry.long_tag_name) # "Event/Sensory-event" + print(entry.takes_value_child) # child "#" entry if tag takes a value + + Attributes: + unit_classes (dict[str, UnitClassEntry]): Unit classes accepted by this + tag\'s value (non-empty only if ``takesValue`` is set). + value_classes (dict[str, HedSchemaEntry]): Value classes that constrain + the value format. + long_tag_name (str): The full slash-separated path from the schema root, + with any trailing ``/#`` stripped. + short_tag_name (str): The final component of the tag path (short form). + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/hed/schema/hed_schema_section.py b/hed/schema/hed_schema_section.py index c410628f..a936cd97 100644 --- a/hed/schema/hed_schema_section.py +++ b/hed/schema/hed_schema_section.py @@ -15,7 +15,39 @@ class HedSchemaSection: - """Container with entries in one section of the schema.""" + """Typed container for all entries in one section of a loaded HED schema. + + A :class:`~hed.schema.HedSchema` is divided into sections (tags, unit + classes, units, value classes, attributes, properties, unit modifiers). + Each section is a ``HedSchemaSection`` that maps lower-cased entry names + to their :class:`~hed.schema.hed_schema_entry.HedSchemaEntry` objects and + tracks which attributes are valid for that section. + + The concrete entry type for each section is determined by + :data:`entries_by_section`: + + +----------------------------+-------------------+ + | Section key | Entry type | + +============================+===================+ + | ``HedSectionKey.Tags`` | HedTagEntry | + | ``HedSectionKey.UnitClasses`` | UnitClassEntry | + | ``HedSectionKey.Units`` | UnitEntry | + | everything else | HedSchemaEntry | + +----------------------------+-------------------+ + + **Use this class directly when you need to:** + + - Iterate over all entries in a specific schema section. + - Build schema comparison or diff tools. + - Access ``valid_attributes`` to determine which attributes are legal for + a given section. + + Attributes: + all_names (dict[str, HedSchemaEntry]): Map from lower-cased name to entry. + all_entries (list[HedSchemaEntry]): Entries in insertion order. + valid_attributes (dict[str, HedSchemaEntry]): Attribute entries that are + declared valid for this section. + """ def __init__(self, section_key, case_sensitive=True): """Construct schema section. diff --git a/hed/schema/schema_validation/__init__.py b/hed/schema/schema_validation/__init__.py index 895977e2..95894ae9 100644 --- a/hed/schema/schema_validation/__init__.py +++ b/hed/schema/schema_validation/__init__.py @@ -24,7 +24,7 @@ get_allowed_characters_by_name, get_problem_indexes, schema_version_for_library, - validate_schema_description_new, - validate_schema_tag_new, - validate_schema_term_new, + validate_schema_description, + validate_schema_tag, + validate_schema_term, ) diff --git a/hed/schema/schema_validation/compliance.py b/hed/schema/schema_validation/compliance.py index 7d45e03b..cdbb90e3 100644 --- a/hed/schema/schema_validation/compliance.py +++ b/hed/schema/schema_validation/compliance.py @@ -35,9 +35,9 @@ from hed.schema.schema_validation.validation_util import ( get_allowed_characters_by_name, get_problem_indexes, - validate_schema_description_new, - validate_schema_tag_new, - validate_schema_term_new, + validate_schema_description, + validate_schema_tag, + validate_schema_term, ) # --------------------------------------------------------------------------- @@ -255,8 +255,8 @@ def check_invalid_characters(self): continue checked += 1 self.error_handler.push_error_context(ErrorContext.SCHEMA_TAG, str(entry)) - validator = validate_schema_tag_new if section_key == HedSectionKey.Tags else validate_schema_term_new - new_issues = validator(entry) + validate_schema_description_new(entry) + validator = validate_schema_tag if section_key == HedSectionKey.Tags else validate_schema_term + new_issues = validator(entry) + validate_schema_description(entry) self.error_handler.add_context_and_filter(new_issues) issues += new_issues self.error_handler.pop_error_context() diff --git a/hed/schema/schema_validation/validation_util.py b/hed/schema/schema_validation/validation_util.py index 2b35434c..46c422e8 100644 --- a/hed/schema/schema_validation/validation_util.py +++ b/hed/schema/schema_validation/validation_util.py @@ -8,7 +8,7 @@ from hed.schema.hed_schema_constants import character_types -def validate_schema_tag_new(hed_entry) -> list[dict]: +def validate_schema_tag(hed_entry) -> list[dict]: """Check tag entry for capitalization and illegal characters. Parameters: @@ -27,11 +27,11 @@ def validate_schema_tag_new(hed_entry) -> list[dict]: issues_list += ErrorHandler.format_error( SchemaWarnings.SCHEMA_INVALID_CAPITALIZATION, hed_term, char_index=0, problem_char=hed_term[0] ) - issues_list += validate_schema_term_new(hed_entry, hed_term) + issues_list += validate_schema_term(hed_entry, hed_term) return issues_list -def validate_schema_term_new(hed_entry, hed_term=None) -> list[dict]: +def validate_schema_term(hed_entry, hed_term=None) -> list[dict]: """Check the term for invalid character issues Parameters: @@ -56,7 +56,7 @@ def validate_schema_term_new(hed_entry, hed_term=None) -> list[dict]: return issues_list -def validate_schema_description_new(hed_entry) -> list[dict]: +def validate_schema_description(hed_entry) -> list[dict]: """Check the description of the entry for invalid character issues Parameters: diff --git a/hed/tools/analysis/event_manager.py b/hed/tools/analysis/event_manager.py index 19bc573f..2cee4425 100644 --- a/hed/tools/analysis/event_manager.py +++ b/hed/tools/analysis/event_manager.py @@ -5,7 +5,7 @@ from hed.errors.exceptions import HedFileError from hed.models.hed_string import HedString -from hed.models.model_constants import DefTagNames +from hed.models.model_constants import DefTagNames, TopTagReturnType from hed.models import df_util from hed.models import string_util from hed.tools.analysis.temporal_event import TemporalEvent @@ -105,7 +105,7 @@ def _extract_temporal_events(self, hed, event_index, onset_dict): if not hed: return group_tuples = hed.find_top_level_tags( - anchor_tags={DefTagNames.ONSET_KEY, DefTagNames.OFFSET_KEY}, include_groups=2 + anchor_tags={DefTagNames.ONSET_KEY, DefTagNames.OFFSET_KEY}, include_groups=TopTagReturnType.BOTH ) to_remove = [] diff --git a/hed/tools/bids/__init__.py b/hed/tools/bids/__init__.py index 6adbb1ab..202099b9 100644 --- a/hed/tools/bids/__init__.py +++ b/hed/tools/bids/__init__.py @@ -5,4 +5,4 @@ from .bids_file_group import BidsFileGroup from .bids_sidecar_file import BidsSidecarFile from .bids_tabular_file import BidsTabularFile -from .bids_util import walk_back, parse_bids_filename, get_candidates, matches_criteria +from .bids_util import parse_bids_filename diff --git a/hed/tools/util/__init__.py b/hed/tools/util/__init__.py index ce400600..10d53be4 100644 --- a/hed/tools/util/__init__.py +++ b/hed/tools/util/__init__.py @@ -1 +1,4 @@ """Data and file handling utilities.""" + +from .data_util import get_new_dataframe, get_value_dict, replace_values, reorder_columns +from .io_util import check_filename, clean_filename, extract_suffix_path, get_file_list, get_path_components diff --git a/hed/validator/hed_validator.py b/hed/validator/hed_validator.py index 2754649a..120c9f51 100644 --- a/hed/validator/hed_validator.py +++ b/hed/validator/hed_validator.py @@ -230,11 +230,11 @@ def _validate_individual_tags_in_hed_string(self, hed_string_obj, allow_placehol list[dict]: The issues associated with the individual tags. Each issue is a dictionary. """ - from hed.models.definition_dict import DefTagNames + from hed.models.model_constants import DefTagNames, TopTagReturnType validation_issues = [] definition_groups = hed_string_obj.find_top_level_tags( - anchor_tags={DefTagNames.DEFINITION_KEY}, include_groups=1 + anchor_tags={DefTagNames.DEFINITION_KEY}, include_groups=TopTagReturnType.GROUPS ) all_definition_groups = [group for sub_group in definition_groups for group in sub_group.get_all_groups()] for group in hed_string_obj.get_all_groups(): diff --git a/hed/validator/spreadsheet_validator.py b/hed/validator/spreadsheet_validator.py index 49b49f59..493fb38e 100644 --- a/hed/validator/spreadsheet_validator.py +++ b/hed/validator/spreadsheet_validator.py @@ -14,7 +14,7 @@ from hed.validator.onset_validator import OnsetValidator from hed.validator.hed_validator import HedValidator from hed.models import df_util -from hed.models.model_constants import DefTagNames +from hed.models.model_constants import DefTagNames, TopTagReturnType PANDAS_COLUMN_PREFIX_TO_IGNORE = "Unnamed: " @@ -285,7 +285,9 @@ def _check_onset_nans(self, onsets, assembled, hed_schema, error_handler, row_ad hed_obj = HedString(value, hed_schema) error_handler.push_error_context(ErrorContext.ROW, index + row_adj) error_handler.push_error_context(ErrorContext.HED_STRING, hed_obj) - for tag in hed_obj.find_top_level_tags(anchor_tags=DefTagNames.TIMELINE_KEYS, include_groups=0): + for tag in hed_obj.find_top_level_tags( + anchor_tags=DefTagNames.TIMELINE_KEYS, include_groups=TopTagReturnType.TAGS + ): issues += error_handler.format_error_with_context(TemporalErrors.TEMPORAL_TAG_NO_TIME, tag=tag) error_handler.pop_error_context() error_handler.pop_error_context() diff --git a/spec_tests/test_hed_cache.py b/spec_tests/test_hed_cache.py index 354ca47d..7c93078d 100644 --- a/spec_tests/test_hed_cache.py +++ b/spec_tests/test_hed_cache.py @@ -148,7 +148,7 @@ def test_schema_load_schema_version_invalid(self): with self.assertRaises(HedFileError) as context8: load_schema_version(["8.1.0", "notreallibrary_1.0.0"]) - self.assertEqual(context8.exception.args[0], "fileNotFound") + self.assertEqual(context8.exception.args[0], HedExceptions.FILE_NOT_FOUND) class TestLibraryDataCache(unittest.TestCase): diff --git a/tests/models/test_column_mapper.py b/tests/models/test_column_mapper.py index 7133d847..9af11d9e 100644 --- a/tests/models/test_column_mapper.py +++ b/tests/models/test_column_mapper.py @@ -1,7 +1,8 @@ import unittest import os -from hed.models import ColumnMapper, ColumnType, HedString +from hed.models.column_mapper import ColumnMapper +from hed.models import ColumnType, HedString from hed.models.sidecar import Sidecar, DefinitionDict from hed.errors import ValidationErrors from hed import load_schema diff --git a/tests/models/test_sidecar.py b/tests/models/test_sidecar.py index bd6f483a..a08a1ae8 100644 --- a/tests/models/test_sidecar.py +++ b/tests/models/test_sidecar.py @@ -3,10 +3,11 @@ import io import shutil -from hed.errors import HedFileError, ValidationErrors, ErrorSeverity +from hed.errors import HedFileError, HedExceptions, ValidationErrors, ErrorSeverity from hed.models import ColumnMetadata, HedString, Sidecar from hed import schema -from hed.models import DefinitionDict, DefinitionEntry +from hed.models import DefinitionDict +from hed.models.definition_entry import DefinitionEntry from hed.errors import ErrorHandler @@ -75,7 +76,7 @@ def test_name(self): invalid_json = "invalidxmlfile.json" with self.assertRaises(HedFileError) as context: Sidecar(invalid_json) - self.assertEqual(context.exception.args[0], "fileNotFound") + self.assertEqual(context.exception.args[0], HedExceptions.FILE_NOT_FOUND) def test_add_json_string(self): with open(self.json_filename) as file: diff --git a/tests/schema/test_schema_validation_util.py b/tests/schema/test_schema_validation_util.py index eb9ca705..d4787c4e 100644 --- a/tests/schema/test_schema_validation_util.py +++ b/tests/schema/test_schema_validation_util.py @@ -14,14 +14,14 @@ def validate_term_base(self, input_text, expected_issues): for text, issues in zip(input_text, expected_issues, strict=False): entry = HedTagEntry(name=text, section=None) entry.short_tag_name = text - test_issues = util.validate_schema_tag_new(entry) + test_issues = util.validate_schema_tag(entry) self.assertCountEqual(issues, test_issues) def validate_desc_base(self, input_descriptions, expected_issues): for description, issues in zip(input_descriptions, expected_issues, strict=False): entry = HedSchemaEntry(name="dummy", section=None) entry.description = description - test_issues = util.validate_schema_description_new(entry) + test_issues = util.validate_schema_description(entry) self.assertCountEqual(issues, test_issues) def test_validate_schema_term(self): diff --git a/tests/tools/analysis/test_temporal_event.py b/tests/tools/analysis/test_temporal_event.py index 380044fa..0c6e5515 100644 --- a/tests/tools/analysis/test_temporal_event.py +++ b/tests/tools/analysis/test_temporal_event.py @@ -3,6 +3,7 @@ from hed.schema.hed_schema_io import load_schema_version from hed.models import HedString, HedGroup, Sidecar, TabularInput +from hed.models.model_constants import TopTagReturnType from hed.tools.analysis.temporal_event import TemporalEvent from hed.tools.analysis.event_manager import EventManager @@ -27,7 +28,7 @@ def setUpClass(cls): def test_constructor_no_group(self): test1 = HedString("(Onset, def/blech)", hed_schema=self.schema) - groups = test1.find_top_level_tags(["onset"], include_groups=1) + groups = test1.find_top_level_tags(["onset"], include_groups=TopTagReturnType.GROUPS) temp_event = TemporalEvent(groups[0], 3, 4.5) self.assertEqual(temp_event.start_index, 3) self.assertEqual(temp_event.start_time, 4.5) @@ -36,7 +37,7 @@ def test_constructor_no_group(self): def test_constructor_group(self): test1 = HedString("(Onset, (Label/Apple, Blue), Def/Blech/54.3)", hed_schema=self.schema) - groups = test1.find_top_level_tags(["onset"], include_groups=1) + groups = test1.find_top_level_tags(["onset"], include_groups=TopTagReturnType.GROUPS) temp_event = TemporalEvent(groups[0], 3, 4.5) self.assertEqual(temp_event.start_index, 3) self.assertEqual(temp_event.start_time, 4.5)