From 8adae8b85905f1390e634cdca10f36ef22eb3fda Mon Sep 17 00:00:00 2001 From: IanCa Date: Wed, 23 Aug 2023 17:44:49 -0500 Subject: [PATCH 1/5] Add conversionFactor support. Improve default_unit handling and API --- hed/errors/error_messages.py | 2 ++ hed/models/hed_tag.py | 50 ++++++++++++++++++++------- hed/schema/hed_schema_entry.py | 36 ++++++++++++++++++-- hed/validator/tag_validator.py | 22 +----------- readthedocs.yml | 5 +-- tests/models/test_hed_tag.py | 49 +++++++++++++++++++------- tests/schema/test_schema_compare.py | 53 +++++------------------------ tests/schema/util_create_schemas.py | 47 +++++++++++++++++++++++++ 8 files changed, 171 insertions(+), 93 deletions(-) create mode 100644 tests/schema/util_create_schemas.py diff --git a/hed/errors/error_messages.py b/hed/errors/error_messages.py index b5dfb6060..e5e51d7c5 100644 --- a/hed/errors/error_messages.py +++ b/hed/errors/error_messages.py @@ -227,6 +227,8 @@ def val_warning_capitalization(tag): @hed_tag_error(ValidationErrors.UNITS_MISSING, default_severity=ErrorSeverity.WARNING) def val_warning_default_units_used(tag, default_unit): + if default_unit is None: + return f"No unit specified on - '{tag}'. Multiple default values exist and cannot be inferred" return f"No unit specified. Using '{default_unit}' as the default - '{tag}'" diff --git a/hed/models/hed_tag.py b/hed/models/hed_tag.py index 9d470002b..7e579815f 100644 --- a/hed/models/hed_tag.py +++ b/hed/models/hed_tag.py @@ -328,7 +328,7 @@ def _calculate_to_canonical_forms(self, hed_schema): return tag_issues def get_stripped_unit_value(self): - """ Return the extension portion without units. + """ Return the extension divided into value and units, if the units are valid. Returns: stripped_unit_value (str): The extension portion with the units removed. @@ -345,6 +345,32 @@ def get_stripped_unit_value(self): return self.extension, None + def value_as_default_unit(self): + """ Returns the value converted to default units if possible. + + Returns None if the units are invalid.(No default unit or invalid) + + Returns: + value (float or None): The extension value as default units. + If there are not default units, returns None. + + Examples: + 'Duration/300 ms' will return .3 + + """ + tag_unit_classes = self.unit_classes + value, _, units = self.extension.rpartition(" ") + if not value: + stripped_value = units + unit = self.default_unit + else: + stripped_value, unit = self._get_tag_units_portion(tag_unit_classes) + + if stripped_value: + if unit.attributes.get("conversionFactor"): + conversion_factor = unit.attributes.get("conversionFactor", 1.0) + return float(stripped_value) * float(conversion_factor) + @property def unit_classes(self): """ Return a dict of all the unit classes this tag accepts. @@ -476,20 +502,19 @@ def get_tag_unit_class_units(self): return units - def get_unit_class_default_unit(self): + @property + def default_unit(self): """ Get the default unit class unit for this tag. + Only a tag with a single unit class can have default units. Returns: - str: The default unit class unit associated with the specific tag or an empty string. - + unit(UnitEntry or None): the default unit entry for this tag, or None """ - default_unit = '' unit_classes = self.unit_classes.values() - if unit_classes: + if len(unit_classes) == 1: first_unit_class_entry = list(unit_classes)[0] default_unit = first_unit_class_entry.has_attribute(HedKey.DefaultUnits, return_value=True) - - return default_unit + return first_unit_class_entry.units.get(default_unit, None) def base_tag_has_attribute(self, tag_attribute): """ Check to see if the tag has a specific attribute. @@ -536,8 +561,9 @@ def _get_tag_units_portion(self, tag_unit_classes): tag_unit_classes (dict): Dictionary of valid UnitClassEntry objects for this tag. Returns: - stripped_value (str): The value with the units removed. - + stripped_value (str or None): The value with the units removed. + This is filled in if there are no units as well. + unit (UnitEntry or None): The matching unit entry if one is found """ value, _, units = self.extension.rpartition(" ") if not units: @@ -548,12 +574,12 @@ def _get_tag_units_portion(self, tag_unit_classes): possible_match = self._find_modifier_unit_entry(units, all_valid_unit_permutations) if possible_match and not possible_match.has_attribute(HedKey.UnitPrefix): - return value, units + return value, possible_match # Repeat the above, but as a prefix possible_match = self._find_modifier_unit_entry(value, all_valid_unit_permutations) if possible_match and possible_match.has_attribute(HedKey.UnitPrefix): - return units, value + return possible_match, value return None, None diff --git a/hed/schema/hed_schema_entry.py b/hed/schema/hed_schema_entry.py index 2ea635050..797bd2460 100644 --- a/hed/schema/hed_schema_entry.py +++ b/hed/schema/hed_schema_entry.py @@ -2,6 +2,8 @@ from hed.schema.hed_schema_constants import HedKey import inflect +import copy + pluralize = inflect.engine() pluralize.defnoun("hertz", "hertz") @@ -133,6 +135,20 @@ def get_known_attributes(self): return {key: value for key, value in self.attributes.items() if not self._unknown_attributes or key not in self._unknown_attributes} + # Give a default deep copy that excludes the _section attribute + def __deepcopy__(self, memo): + # Create a new instance + new_obj = self.__class__.__new__(self.__class__) + memo[id(self)] = new_obj # Add the new object to the memo to handle cyclic references + + for k, v in self.__dict__.items(): + if k != "_section": + new_val = copy.deepcopy(v, memo) + else: + new_val = v + setattr(new_obj, k, new_val) + return new_obj + class UnitClassEntry(HedSchemaEntry): """ A single unit class entry in the HedSchema. """ @@ -169,7 +185,11 @@ def finalize_entry(self, schema): for derived_unit in new_derivative_units: derivative_units[derived_unit] = unit_entry for modifier in unit_entry.unit_modifiers: - derivative_units[modifier.name + derived_unit] = unit_entry + new_entry = copy.deepcopy(unit_entry) + derivative_units[modifier.name + derived_unit] = new_entry + new_entry.unit_class_name = derived_unit + new_entry.attributes["conversionFactor"] = new_entry.get_conversion_factor(modifier_entry=modifier) + self.derivative_units = derivative_units def __eq__(self, other): @@ -182,7 +202,6 @@ def __eq__(self, other): class UnitEntry(HedSchemaEntry): """ A single unit entry with modifiers in the HedSchema. """ - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.unit_class_name = None @@ -197,6 +216,19 @@ def finalize_entry(self, schema): """ self.unit_modifiers = schema._get_modifiers_for_unit(self.name) + def get_conversion_factor(self, modifier_entry): + """Returns the conversion factor from combining this unit with the specified modifier + + Parameters: + modifier_entry (HedSchemaEntry): The modifier to apply + + Returns: + conversion_factor(float): Defaults to 1.0 conversion factor if not present on unit and modifier. + """ + base_factor = float(self.attributes.get("conversionFactor", "1.0").replace("^", "e")) + modifier_factor = float(modifier_entry.attributes.get("conversionFactor", "1.0").replace("^", "e")) + return base_factor * modifier_factor + class HedTagEntry(HedSchemaEntry): """ A single tag entry in the HedSchema. """ diff --git a/hed/validator/tag_validator.py b/hed/validator/tag_validator.py index 586d823da..f0d585a70 100644 --- a/hed/validator/tag_validator.py +++ b/hed/validator/tag_validator.py @@ -307,7 +307,7 @@ def _check_units(self, original_tag, bad_units, report_as): validation_issue = ErrorHandler.format_error(ValidationErrors.UNITS_INVALID, tag=report_as, units=tag_unit_class_units) else: - default_unit = original_tag.get_unit_class_default_unit() + default_unit = original_tag.default_unit validation_issue = ErrorHandler.format_error(ValidationErrors.UNITS_MISSING, tag=report_as, default_unit=default_unit) return validation_issue @@ -378,26 +378,6 @@ def check_tag_requires_child(self, original_tag): tag=original_tag) return validation_issues - def check_tag_unit_class_units_exist(self, original_tag): - """ Report warning if tag has a unit class tag with no units. - - Parameters: - original_tag (HedTag): The original tag that is used to report the error. - - Returns: - list: Validation issues. Each issue is a dictionary. - - """ - validation_issues = [] - if original_tag.is_unit_class_tag(): - tag_unit_values = original_tag.extension - if tag_validator_util.validate_numeric_value_class(tag_unit_values): - default_unit = original_tag.get_unit_class_default_unit() - validation_issues += ErrorHandler.format_error(ValidationErrors.UNITS_MISSING, - tag=original_tag, - default_unit=default_unit) - return validation_issues - def check_for_invalid_extension_chars(self, original_tag): """Report invalid characters in extension/value. diff --git a/readthedocs.yml b/readthedocs.yml index 3a0eed7e6..bf5d7274d 100644 --- a/readthedocs.yml +++ b/readthedocs.yml @@ -5,7 +5,9 @@ formats: - pdf build: - image: latest + os: "ubuntu-22.04" + tools: + python: "3.8" # Build documentation in the docs/ directory with Sphinx sphinx: @@ -15,7 +17,6 @@ sphinx: python: - version: 3.8 install: - requirements: docs/requirements.txt system_packages: true diff --git a/tests/models/test_hed_tag.py b/tests/models/test_hed_tag.py index 50b1e1587..97e0ee247 100644 --- a/tests/models/test_hed_tag.py +++ b/tests/models/test_hed_tag.py @@ -1,10 +1,16 @@ from hed.models.hed_tag import HedTag from tests.validator.test_tag_validator_base import TestHedBase from hed.schema import HedKey +from hed import load_schema_version + +from tests.schema import util_create_schemas class TestValidatorUtilityFunctions(TestHedBase): - schema_file = '../data/schema_tests/HED8.0.0t.xml' + + @classmethod + def setUpClass(cls): + cls.hed_schema = load_schema_version("8.2.0") def test_if_tag_exists(self): valid_tag1 = HedTag('Left-handed', hed_schema=self.hed_schema) @@ -37,7 +43,9 @@ def test_if_tag_exists(self): class TestSchemaUtilityFunctions(TestHedBase): - schema_file = '../data/schema_tests/HED8.0.0t.xml' + @classmethod + def setUpClass(cls): + cls.hed_schema = load_schema_version("8.2.0") def test_correctly_determine_tag_takes_value(self): value_tag1 = HedTag('Distance/35 px', hed_schema=self.hed_schema) @@ -65,14 +73,14 @@ def test_should_determine_default_unit(self): # schema=self.schema) no_unit_class_tag = HedTag('RGB-red/0.5', hed_schema=self.hed_schema) no_value_tag = HedTag('Black', hed_schema=self.hed_schema) - unit_class_tag1_result = unit_class_tag1.get_unit_class_default_unit() - # unit_class_tag2_result = unit_class_tag2.get_unit_class_default_unit() - no_unit_class_tag_result = no_unit_class_tag.get_unit_class_default_unit() - no_value_tag_result = no_value_tag.get_unit_class_default_unit() - self.assertEqual(unit_class_tag1_result, 's') + unit_class_tag1_result = unit_class_tag1.default_unit + # unit_class_tag2_result = unit_class_tag2.default_unit + no_unit_class_tag_result = no_unit_class_tag.default_unit + no_value_tag_result = no_value_tag.default_unit + self.assertEqual(unit_class_tag1_result.name, 's') # self.assertEqual(unit_class_tag2_result, '$') - self.assertEqual(no_unit_class_tag_result, '') - self.assertEqual(no_value_tag_result, '') + self.assertEqual(no_unit_class_tag_result, None) + self.assertEqual(no_value_tag_result, None) def test_correctly_determine_tag_unit_classes(self): unit_class_tag1 = HedTag('distance/35 px', hed_schema=self.hed_schema) @@ -97,13 +105,14 @@ def test_determine_tags_legal_units(self): unit_class_tag1_result = unit_class_tag1.get_tag_unit_class_units() # unit_class_tag2_result = unit_class_tag2.get_tag_unit_class_units() no_unit_class_tag_result = no_unit_class_tag.get_tag_unit_class_units() - self.assertCountEqual(unit_class_tag1_result, [ + self.assertCountEqual(sorted(unit_class_tag1_result), sorted([ 'inch', 'm', 'foot', 'metre', + 'meter', 'mile', - ]) + ])) # self.assertCountEqual(unit_class_tag2_result, [ # 'dollar', # '$', @@ -160,4 +169,20 @@ def test_determine_allows_extensions(self): self.assertEqual(extension_tag1_result, True) self.assertEqual(no_extension_tag1_result, False) self.assertEqual(no_extension_tag2_result, False) - self.assertEqual(no_extension_tag3_result, False) \ No newline at end of file + self.assertEqual(no_extension_tag3_result, False) + + def test_get_as_default_units(self): + tag = HedTag("Duration/300 ms", hed_schema=self.hed_schema) + self.assertAlmostEqual(0.3, tag.value_as_default_unit()) + + tag2 = HedTag("Duration/300", hed_schema=self.hed_schema) + self.assertAlmostEqual(300, tag2.value_as_default_unit()) + + tag3 = HedTag("Duration/300 m", hed_schema=self.hed_schema) + self.assertEqual(None, tag3.value_as_default_unit()) + + tag4 = HedTag("IntensityTakesValue/300", hed_schema=util_create_schemas.load_schema_intensity()) + self.assertEqual(300, tag4.value_as_default_unit()) + + tag5 = HedTag("IntensityTakesValue/300 cd", hed_schema=util_create_schemas.load_schema_intensity()) + self.assertEqual(None, tag5.value_as_default_unit()) diff --git a/tests/schema/test_schema_compare.py b/tests/schema/test_schema_compare.py index 6356f67e8..f6b1ceed1 100644 --- a/tests/schema/test_schema_compare.py +++ b/tests/schema/test_schema_compare.py @@ -1,54 +1,19 @@ import unittest import json -from hed.schema import HedKey, HedSectionKey, from_string +from hed.schema import HedKey, HedSectionKey from hed.schema.schema_compare import compare_schemas, find_matching_tags, \ _pretty_print_diff_all, _pretty_print_missing_all, compare_differences from hed import load_schema_version +from . import util_create_schemas -class TestSchemaComparison(unittest.TestCase): - library_schema_start = """HED library="testcomparison" version="1.1.0" withStandard="8.2.0" unmerged="true" - -'''Prologue''' - -!# start schema - -""" - - library_schema_end = """ -!# end schema - -!# end hed - """ - - def _get_test_schema(self, node_lines): - library_schema_string = self.library_schema_start + "\n".join(node_lines) + self.library_schema_end - test_schema = from_string(library_schema_string, ".mediawiki") - - return test_schema - - def load_schema1(self): - test_nodes = ["'''TestNode''' [This is a simple test node]\n", - " *TestNode2", - " *TestNode3", - " *TestNode4" - ] - return self._get_test_schema(test_nodes) - - def load_schema2(self): - test_nodes = ["'''TestNode''' [This is a simple test node]\n", - " *TestNode2", - " **TestNode3", - " *TestNode5" - ] - - return self._get_test_schema(test_nodes) +class TestSchemaComparison(unittest.TestCase): def test_find_matching_tags(self): # create entries for schema1 - schema1 = self.load_schema1() - schema2 = self.load_schema2() + schema1 = util_create_schemas.load_schema1() + schema2 = util_create_schemas.load_schema2() result = find_matching_tags(schema1, schema2) # Check if the result is correct @@ -82,8 +47,8 @@ def test_find_matching_tags(self): self.assertNotIn("summary", json_style_dict_no_summary) def test_compare_schemas(self): - schema1 = self.load_schema1() - schema2 = self.load_schema2() + schema1 = util_create_schemas.load_schema1() + schema2 = util_create_schemas.load_schema2() matches, not_in_schema1, not_in_schema2, unequal_entries = compare_schemas(schema1, schema2) @@ -103,8 +68,8 @@ def test_compare_schemas(self): self.assertIn("TestNode3", unequal_entries[HedSectionKey.Tags]) def test_compare_differences(self): - schema1 = self.load_schema1() - schema2 = self.load_schema2() + schema1 = util_create_schemas.load_schema1() + schema2 = util_create_schemas.load_schema2() not_in_schema1, not_in_schema2, unequal_entries = compare_differences(schema1, schema2) diff --git a/tests/schema/util_create_schemas.py b/tests/schema/util_create_schemas.py new file mode 100644 index 000000000..850d014eb --- /dev/null +++ b/tests/schema/util_create_schemas.py @@ -0,0 +1,47 @@ +from hed.schema import HedKey, HedSectionKey, from_string + + +library_schema_start = """HED library="testcomparison" version="1.1.0" withStandard="8.2.0" unmerged="true" + +'''Prologue''' + +!# start schema + +""" + +library_schema_end = """ +!# end schema + +!# end hed + """ + +def _get_test_schema(node_lines): + library_schema_string = library_schema_start + "\n".join(node_lines) + library_schema_end + test_schema = from_string(library_schema_string, ".mediawiki") + + return test_schema + + +def load_schema1(): + test_nodes = ["'''TestNode''' [This is a simple test node]\n", + " *TestNode2", + " *TestNode3", + " *TestNode4" + ] + return _get_test_schema(test_nodes) + + +def load_schema2(): + test_nodes = ["'''TestNode''' [This is a simple test node]\n", + " *TestNode2", + " **TestNode3", + " *TestNode5" + ] + + return _get_test_schema(test_nodes) + + +def load_schema_intensity(): + test_nodes = ["'''IntensityTakesValue'''", + " * # {unitClass=intensityUnits}"] + return _get_test_schema(test_nodes) \ No newline at end of file From d7055cca40d860c2153642c66b9343b53fae1818 Mon Sep 17 00:00:00 2001 From: IanCa Date: Fri, 25 Aug 2023 18:19:39 -0500 Subject: [PATCH 2/5] Update searching. Add optional field to exact match notation {required_expression: optional_expression}. Ban negation of wildcards due to poorly defined behavior. --- hed/models/expression_parser.py | 40 +++++++++++++++++++++++--- tests/models/test_expression_parser.py | 37 +++++++++++++++++++++++- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/hed/models/expression_parser.py b/hed/models/expression_parser.py index d976fe6b8..736ed625b 100644 --- a/hed/models/expression_parser.py +++ b/hed/models/expression_parser.py @@ -64,6 +64,7 @@ class Token: Wildcard = 10 ExactMatch = 11 ExactMatchEnd = 12 + ExactMatchOptional = 14 NotInLine = 13 # Not currently a token. In development and may become one. def __init__(self, text): @@ -83,6 +84,7 @@ def __init__(self, text): "???": Token.Wildcard, # Any Group "{": Token.ExactMatch, # Nothing else "}": Token.ExactMatchEnd, # Nothing else + ":": Token.ExactMatchOptional, "@": Token.NotInLine } self.kind = tokens.get(text, Token.Tag) @@ -158,7 +160,11 @@ def handle_expr(self, hed_group, exact=False): if not groups1: return groups1 groups2 = self.right.handle_expr(hed_group, exact=exact) - # this is slow... + + return self.merge_groups(groups1, groups2) + + @staticmethod + def merge_groups(groups1, groups2): return_list = [] for group in groups1: for other_group in groups2: @@ -308,6 +314,20 @@ def handle_expr(self, hed_group, exact=False): if return_list: return return_list + # Basically if we don't have an exact match above, do the more complex matching including optional + if self.left: + optional_groups = self.left.handle_expr(hed_group, exact=True) + found_groups = ExpressionAnd.merge_groups(found_groups, optional_groups) + + if found_groups: + return_list = [] + for group in found_groups: + if len(group.group.children) == len(group.tags): + return_list.append(group) + + if return_list: + return return_list + return [] @@ -336,6 +356,11 @@ def __init__(self, expression_string): '[[Event and Action]]' - Find a group with Event And Action at the same level. + Practical Complex Example: + + [[{(Onset or Offset), (Def or [[Def-expand]]): ???}]] - A group with an onset tag, + a def tag or def-expand group, and an optional wildcard group + Parameters: expression_string(str): The query string """ @@ -382,6 +407,9 @@ def _handle_negation(self): next_token = self._next_token_is([Token.LogicalNegation]) if next_token == Token.LogicalNegation: interior = self._handle_grouping_op() + if "?" in str(interior): + raise ValueError("Cannot negate wildcards, or expressions that contain wildcards." + "Use {required_expression : optional_expression}.") expr = ExpressionNegation(next_token, right=interior) return expr else: @@ -411,8 +439,12 @@ def _handle_grouping_op(self): elif next_token == Token.ExactMatch: interior = self._handle_and_op() expr = ExpressionExactMatch(next_token, right=interior) - next_token = self._next_token_is([Token.ExactMatchEnd]) - if next_token != Token.ExactMatchEnd: + next_token = self._next_token_is([Token.ExactMatchEnd, Token.ExactMatchOptional]) + if next_token == Token.ExactMatchOptional: + optional_portion = self._handle_and_op() + expr.left = optional_portion + next_token = self._next_token_is([Token.ExactMatchEnd]) + if next_token is None: raise ValueError("Parse error: Missing closing curly bracket") else: next_token = self._get_next_token() @@ -434,7 +466,7 @@ def _parse(self, expression_string): return expr def _tokenize(self, expression_string): - grouping_re = r"\[\[|\[|\]\]|\]|}|{" + grouping_re = r"\[\[|\[|\]\]|\]|}|{|:" paren_re = r"\)|\(|~" word_re = r"\?+|\band\b|\bor\b|,|[\"_\-a-zA-Z0-9/.^#\*@]+" re_string = fr"({grouping_re}|{paren_re}|{word_re})" diff --git a/tests/models/test_expression_parser.py b/tests/models/test_expression_parser.py index 926338d8f..808114cf6 100644 --- a/tests/models/test_expression_parser.py +++ b/tests/models/test_expression_parser.py @@ -694,4 +694,39 @@ def test_not_in_line3(self): "(A, B, (C)), D": True, "(A, B, (C)), (D), E": True, } - self.base_test("@C or B", test_strings) \ No newline at end of file + self.base_test("@C or B", test_strings) + + def test_optional_exact_group(self): + test_strings = { + "A, C": True, + } + self.base_test("{a and (b or c)}", test_strings) + + test_strings = { + "A, B, C, D": True, + } + self.base_test("{a and b: c and d}", test_strings) + + test_strings = { + "A, B, C": True, + "A, B, C, D": False, + } + self.base_test("{a and b: c or d}", test_strings) + + test_strings = { + "A, C": True, + "A, D": True, + "A, B, C": False, + "A, B, C, D": False, + } + self.base_test("{a or b: c or d}", test_strings) + + test_strings = { + "(Onset, (Def-expand/taco))": True, + "(Onset, (Def-expand/taco, (Label/DefContents)))": True, + "(Onset, (Def-expand/taco), (Label/OnsetContents))": True, + "(Onset, (Def-expand/taco), (Label/OnsetContents, Description/MoreContents))": True, + "Onset, (Def-expand/taco), (Label/OnsetContents)": False, + "(Onset, (Def-expand/taco), Label/OnsetContents)": False, + } + self.base_test("[[{(Onset or Offset), (Def or [[Def-expand]]): ???}]]", test_strings) \ No newline at end of file From 57f8a46bfd4e84918b7b034b486518220738e8fe Mon Sep 17 00:00:00 2001 From: IanCa Date: Sun, 27 Aug 2023 11:50:43 -0500 Subject: [PATCH 3/5] Tweak so it's easier to validate hed strings --- hed/models/hed_string.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hed/models/hed_string.py b/hed/models/hed_string.py index 328316868..d33c376b0 100644 --- a/hed/models/hed_string.py +++ b/hed/models/hed_string.py @@ -37,6 +37,7 @@ def __init__(self, hed_string, hed_schema, def_dict=None, _contents=None): super().__init__(hed_string, contents=contents, startpos=0, endpos=len(hed_string)) self._schema = hed_schema self._from_strings = None + self._def_dict = def_dict @classmethod def from_hed_strings(cls, hed_strings): @@ -344,7 +345,7 @@ def validate(self, allow_placeholders=True, error_handler=None): """ from hed.validator import HedValidator - validator = HedValidator(self._schema) + validator = HedValidator(self._schema, def_dicts=self._def_dict) return validator.validate(self, allow_placeholders=allow_placeholders, error_handler=error_handler) def find_top_level_tags(self, anchor_tags, include_groups=2): From 1049bc567893e5747e18a74d4ddc2d66c48b227f Mon Sep 17 00:00:00 2001 From: IanCa Date: Sun, 27 Aug 2023 11:52:54 -0500 Subject: [PATCH 4/5] Also add def_dict when creating from hed strings --- hed/models/hed_string.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hed/models/hed_string.py b/hed/models/hed_string.py index d33c376b0..fe559689c 100644 --- a/hed/models/hed_string.py +++ b/hed/models/hed_string.py @@ -56,7 +56,8 @@ def from_hed_strings(cls, hed_strings): hed_string = ",".join([group._hed_string for group in hed_strings]) contents = [child for sub_string in hed_strings for child in sub_string.children] first_schema = hed_strings[0]._schema - new_string.__init__(hed_string=hed_string, _contents=contents, hed_schema=first_schema) + first_dict = hed_strings[0]._def_dict + new_string.__init__(hed_string=hed_string, _contents=contents, hed_schema=first_schema, def_dict=first_dict) new_string._from_strings = hed_strings return new_string From 3a59047397f15ce3aabc28955f73ca40d6a6d11c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 22:46:06 +0000 Subject: [PATCH 5/5] Bump spec_tests/hed-specification from `8e78802` to `25a8b2a` Bumps [spec_tests/hed-specification](https://github.com/hed-standard/hed-specification) from `8e78802` to `25a8b2a`. - [Release notes](https://github.com/hed-standard/hed-specification/releases) - [Commits](https://github.com/hed-standard/hed-specification/compare/8e788027b4b7f2bd7b2c7af2f52b0ecb2f4a9867...25a8b2a69fe01b745c3f0fc8f4fa56d5b7e12aeb) --- updated-dependencies: - dependency-name: spec_tests/hed-specification dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- spec_tests/hed-specification | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec_tests/hed-specification b/spec_tests/hed-specification index 8e788027b..25a8b2a69 160000 --- a/spec_tests/hed-specification +++ b/spec_tests/hed-specification @@ -1 +1 @@ -Subproject commit 8e788027b4b7f2bd7b2c7af2f52b0ecb2f4a9867 +Subproject commit 25a8b2a69fe01b745c3f0fc8f4fa56d5b7e12aeb