Skip to content

Commit b2c4373

Browse files
committed
Minor cleanup and corrections
1 parent 656f251 commit b2c4373

5 files changed

Lines changed: 107 additions & 97 deletions

File tree

docs/api/errors.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ Error functions
4040

4141
.. autofunction:: hed.errors.error_reporter.sort_issues
4242

43-
.. autofunction:: hed.errors.error_reporter.replace_tag_references
43+
.. autofunction:: hed.errors.error_reporter.separate_issues
44+
45+
.. autofunction:: hed.errors.error_reporter.iter_errors
4446

4547
Error types
4648
-----------

hed/errors/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
"""Error handling module for HED."""
22

3-
from .error_reporter import ErrorHandler, get_printable_issue_string, sort_issues, replace_tag_references, iter_errors
3+
from .error_reporter import (
4+
ErrorHandler,
5+
separate_issues,
6+
get_printable_issue_string,
7+
sort_issues,
8+
iter_errors,
9+
)
410
from .error_types import (
511
DefinitionErrors,
612
TemporalErrors,

hed/errors/error_reporter.py

Lines changed: 83 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,23 @@ def wrapper(tag, *args, severity=default_severity, **kwargs):
204204
schema_error_messages.mark_as_used = True
205205

206206

207+
def separate_issues(issues_list: list[dict]) -> tuple[list[dict], list[dict]]:
208+
"""Separate a list of issues into errors and warnings.
209+
210+
Parameters:
211+
issues_list (list[dict]): A list of issue dictionaries. The 'severity' key is
212+
optional; issues that omit it are treated as errors (ErrorSeverity.ERROR).
213+
214+
Returns:
215+
tuple[list[dict], list[dict]]: A tuple of (errors, warnings) where errors contains
216+
issues with severity <= ErrorSeverity.ERROR and warnings contains issues with
217+
severity > ErrorSeverity.ERROR.
218+
"""
219+
errors = [issue for issue in issues_list if issue.get("severity", ErrorSeverity.ERROR) <= ErrorSeverity.ERROR]
220+
warnings = [issue for issue in issues_list if issue.get("severity", ErrorSeverity.ERROR) > ErrorSeverity.ERROR]
221+
return errors, warnings
222+
223+
207224
class ErrorHandler:
208225
"""Class to hold error context and having general error functions."""
209226

@@ -274,37 +291,6 @@ def format_error_with_context(self, *args, **kwargs):
274291

275292
return error_object
276293

277-
@staticmethod
278-
def filter_issues_by_severity(issues_list: list[dict], severity: int) -> list[dict]:
279-
"""Gather all issues matching or below a given severity.
280-
281-
Parameters:
282-
issues_list (list[dict]): A list of dictionaries containing the full issue list.
283-
severity (int): The level of issues to keep.
284-
285-
Returns:
286-
list[dict]: A list of dictionaries containing the issue list after filtering by severity.
287-
288-
"""
289-
return [issue for issue in issues_list if issue.get("severity", ErrorSeverity.ERROR) <= severity]
290-
291-
@staticmethod
292-
def separate_issues(issues_list: list[dict]) -> tuple[list[dict], list[dict]]:
293-
"""Separate a list of issues into errors and warnings.
294-
295-
Parameters:
296-
issues_list (list[dict]): A list of issue dictionaries. The 'severity' key is
297-
optional; issues that omit it are treated as errors (ErrorSeverity.ERROR).
298-
299-
Returns:
300-
tuple[list[dict], list[dict]]: A tuple of (errors, warnings) where errors contains
301-
issues with severity <= ErrorSeverity.ERROR and warnings contains issues with
302-
severity > ErrorSeverity.ERROR.
303-
"""
304-
errors = [issue for issue in issues_list if issue.get("severity", ErrorSeverity.ERROR) <= ErrorSeverity.ERROR]
305-
warnings = [issue for issue in issues_list if issue.get("severity", ErrorSeverity.ERROR) > ErrorSeverity.ERROR]
306-
return errors, warnings
307-
308294
@staticmethod
309295
def format_error(error_type: str, *args, actual_error=None, **kwargs) -> list[dict]:
310296
"""Format an error based on the parameters, which vary based on what type of error this is.
@@ -440,13 +426,46 @@ def val_error_unknown(*args, **kwargs) -> str:
440426
return f"Unknown error. Args: {str(args), str(kwargs)}"
441427

442428
@staticmethod
443-
def filter_issues_by_count(issues, count, by_file=False) -> tuple[list[dict], dict[str, int]]:
429+
def filter_issues_by_severity(issues_list: list[dict], severity: int) -> list[dict]:
430+
"""Gather all issues matching or below a given severity.
431+
432+
Parameters:
433+
issues_list (list[dict]): A list of dictionaries containing the full issue list.
434+
severity (int): The level of issues to keep.
435+
436+
Returns:
437+
list[dict]: A list of dictionaries containing the issue list after filtering by severity.
438+
439+
"""
440+
return [issue for issue in issues_list if issue.get("severity", ErrorSeverity.ERROR) <= severity]
441+
442+
@staticmethod
443+
def aggregate_code_counts(file_code_dict: dict) -> dict:
444+
"""Aggregate the counts of codes across multiple files.
445+
446+
Parameters:
447+
file_code_dict (dict): A dictionary where keys are filenames and values are
448+
dictionaries of code counts.
449+
450+
Returns:
451+
dict: A dictionary with the aggregated counts of codes across all files.
452+
"""
453+
total_counts = defaultdict(int)
454+
for file_dict in file_code_dict.values():
455+
for code, count in file_dict.items():
456+
total_counts[code] += count
457+
return dict(total_counts)
458+
459+
@staticmethod
460+
def filter_issues_by_count(
461+
issues: list[dict], count: int, by_file: bool = False
462+
) -> tuple[list[dict], dict[str, int]]:
444463
"""Filter the issues list to only include the first count issues of each code.
445464
446-
Parameters:
447-
issues (list): A list of dictionaries containing the full issue list.
448-
count (int): The number of issues to keep for each code.
449-
by_file (bool): If True, group by file name.
465+
Parameters:
466+
issues (list[dict]): A list of dictionaries containing the full issue list.
467+
count (int): The number of issues to keep for each code.
468+
by_file (bool): If True, group by file name.
450469
451470
Returns:
452471
tuple[list[dict], dict[str, int]]: A tuple containing:
@@ -474,22 +493,6 @@ def filter_issues_by_count(issues, count, by_file=False) -> tuple[list[dict], di
474493

475494
return filtered_issues, ErrorHandler.aggregate_code_counts(file_dicts)
476495

477-
@staticmethod
478-
def aggregate_code_counts(file_code_dict) -> dict:
479-
"""Aggregate the counts of codes across multiple files.
480-
481-
Parameters:
482-
file_code_dict (dict): A dictionary where keys are filenames and values are dictionaries of code counts.
483-
484-
Returns:
485-
dict: A dictionary with the aggregated counts of codes across all files.
486-
"""
487-
total_counts = defaultdict(int)
488-
for file_dict in file_code_dict.values():
489-
for code, count in file_dict.items():
490-
total_counts[code] += count
491-
return dict(total_counts)
492-
493496
@staticmethod
494497
def get_code_counts(issues: list[dict]) -> dict[str, int]:
495498
"""Count the occurrences of each error code in the issues list.
@@ -507,6 +510,34 @@ def get_code_counts(issues: list[dict]) -> dict[str, int]:
507510
code_counts[code] += 1
508511
return dict(code_counts)
509512

513+
@staticmethod
514+
def replace_tag_references(list_or_dict):
515+
"""Utility function to remove any references to tags, strings, etc. from any type of nested list or dict.
516+
517+
Use this if you want to save out issues to a file.
518+
519+
If you'd prefer a copy returned, use ErrorHandler.replace_tag_references(list_or_dict.copy()).
520+
521+
Parameters:
522+
list_or_dict (list or dict): An arbitrarily nested list/dict structure
523+
"""
524+
if isinstance(list_or_dict, dict):
525+
for key, value in list_or_dict.items():
526+
if isinstance(value, (dict, list)):
527+
ErrorHandler.replace_tag_references(value)
528+
elif isinstance(value, (bool, float, int)):
529+
list_or_dict[key] = value
530+
else:
531+
list_or_dict[key] = str(value)
532+
elif isinstance(list_or_dict, list):
533+
for key, value in enumerate(list_or_dict):
534+
if isinstance(value, (dict, list)):
535+
ErrorHandler.replace_tag_references(value)
536+
elif isinstance(value, (bool, float, int)):
537+
list_or_dict[key] = value
538+
else:
539+
list_or_dict[key] = str(value)
540+
510541

511542
def sort_issues(issues, reverse=False) -> list[dict]:
512543
"""Sort a list of issues by the error context values.
@@ -839,31 +870,3 @@ def _create_error_tree(error_dict, parent_element=None, add_link=True):
839870
_create_error_tree(value, context_ul, add_link)
840871

841872
return parent_element
842-
843-
844-
def replace_tag_references(list_or_dict):
845-
"""Utility function to remove any references to tags, strings, etc. from any type of nested list or dict.
846-
847-
Use this if you want to save out issues to a file.
848-
849-
If you'd prefer a copy returned, use replace_tag_references(list_or_dict.copy()).
850-
851-
Parameters:
852-
list_or_dict (list or dict): An arbitrarily nested list/dict structure
853-
"""
854-
if isinstance(list_or_dict, dict):
855-
for key, value in list_or_dict.items():
856-
if isinstance(value, (dict, list)):
857-
replace_tag_references(value)
858-
elif isinstance(value, (bool, float, int)):
859-
list_or_dict[key] = value
860-
else:
861-
list_or_dict[key] = str(value)
862-
elif isinstance(list_or_dict, list):
863-
for key, value in enumerate(list_or_dict):
864-
if isinstance(value, (dict, list)):
865-
replace_tag_references(value)
866-
elif isinstance(value, (bool, float, int)):
867-
list_or_dict[key] = value
868-
else:
869-
list_or_dict[key] = str(value)

hed/scripts/schema_script_util.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
from collections import defaultdict
33
from hed.schema import from_string, load_schema, from_dataframes
44
from hed.schema import hed_cache
5-
from hed.errors import get_printable_issue_string, HedFileError
6-
from hed.errors.error_reporter import separate_issues
5+
from hed.errors import get_printable_issue_string, separate_issues, HedFileError
76
from hed.schema.schema_comparer import SchemaComparer
87

98
all_extensions = [".tsv", ".mediawiki", ".xml", ".json"]
@@ -48,10 +47,10 @@ def validate_schema_object(base_schema, schema_name, check_warnings=False):
4847
"""
4948
validation_issues = []
5049
try:
51-
issues = base_schema.check_compliance(check_warnings=check_warnings)
50+
issues = base_schema.check_compliance(check_for_warnings=check_warnings)
5251
if issues and check_warnings:
5352
errors, warnings = separate_issues(issues)
54-
issues = errors + issues
53+
issues = errors + warnings
5554
else:
5655
errors = issues
5756

tests/errors/test_error_reporter.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
SchemaWarnings,
88
get_printable_issue_string,
99
sort_issues,
10-
replace_tag_references,
10+
separate_issues,
1111
)
1212
from hed.errors.error_reporter import hed_tag_error, get_printable_issue_string_html, iter_errors
1313
from hed import HedString, HedTag
@@ -230,17 +230,17 @@ def test_replace_tag_references(self):
230230
"b": {"c": 2, "d": [3, {"e": HedString("Hed2", self._schema)}]},
231231
"f": [5, 6],
232232
}
233-
replace_tag_references(nested_dict)
233+
ErrorHandler.replace_tag_references(nested_dict)
234234
self.assertEqual(nested_dict, {"a": "Hed1", "b": {"c": 2, "d": [3, {"e": "Hed2"}]}, "f": [5, 6]})
235235

236236
# Test with mixed data types and HedString in a nested list
237237
nested_list = [HedString("Hed1", self._schema), {"a": 2, "b": [3, {"c": HedString("Hed2", self._schema)}]}]
238-
replace_tag_references(nested_list)
238+
ErrorHandler.replace_tag_references(nested_list)
239239
self.assertEqual(nested_list, ["Hed1", {"a": 2, "b": [3, {"c": "Hed2"}]}])
240240

241241
# Test with mixed data types and HedString in a list within a dict
242242
mixed = {"a": HedString("Hed1", self._schema), "b": [2, 3, {"c": HedString("Hed2", self._schema)}, 4]}
243-
replace_tag_references(mixed)
243+
ErrorHandler.replace_tag_references(mixed)
244244
self.assertEqual(mixed, {"a": "Hed1", "b": [2, 3, {"c": "Hed2"}, 4]})
245245

246246
def test_register_error_twice(self):
@@ -302,26 +302,26 @@ def test_get_code_counts(self):
302302

303303

304304
class TestSeparateIssues(unittest.TestCase):
305-
"""Tests for ErrorHandler.separate_issues."""
305+
"""Tests for separate_issues."""
306306

307307
@staticmethod
308308
def _make_issue(severity):
309309
return {"severity": severity, "message": "test"}
310310

311311
def test_empty_list(self):
312-
errors, warnings = ErrorHandler.separate_issues([])
312+
errors, warnings = separate_issues([])
313313
self.assertEqual(errors, [])
314314
self.assertEqual(warnings, [])
315315

316316
def test_only_errors(self):
317317
issues = [self._make_issue(ErrorSeverity.ERROR), self._make_issue(ErrorSeverity.ERROR)]
318-
errors, warnings = ErrorHandler.separate_issues(issues)
318+
errors, warnings = separate_issues(issues)
319319
self.assertEqual(len(errors), 2)
320320
self.assertEqual(len(warnings), 0)
321321

322322
def test_only_warnings(self):
323323
issues = [self._make_issue(ErrorSeverity.WARNING), self._make_issue(ErrorSeverity.WARNING)]
324-
errors, warnings = ErrorHandler.separate_issues(issues)
324+
errors, warnings = separate_issues(issues)
325325
self.assertEqual(len(errors), 0)
326326
self.assertEqual(len(warnings), 2)
327327

@@ -331,18 +331,18 @@ def test_mixed(self):
331331
self._make_issue(ErrorSeverity.WARNING),
332332
self._make_issue(ErrorSeverity.ERROR),
333333
]
334-
errors, warnings = ErrorHandler.separate_issues(issues)
334+
errors, warnings = separate_issues(issues)
335335
self.assertEqual(len(errors), 2)
336336
self.assertEqual(len(warnings), 1)
337337

338338
def test_original_list_unchanged(self):
339339
issues = [self._make_issue(ErrorSeverity.ERROR), self._make_issue(ErrorSeverity.WARNING)]
340-
ErrorHandler.separate_issues(issues)
340+
separate_issues(issues)
341341
self.assertEqual(len(issues), 2)
342342

343343
def test_missing_severity_treated_as_error(self):
344344
"""Issues without a 'severity' key should be treated as errors, not raise KeyError."""
345345
issues = [{"message": "no severity"}, self._make_issue(ErrorSeverity.WARNING)]
346-
errors, warnings = ErrorHandler.separate_issues(issues)
346+
errors, warnings = separate_issues(issues)
347347
self.assertEqual(len(errors), 1, "Issue missing severity should default to ERROR")
348348
self.assertEqual(len(warnings), 1)

0 commit comments

Comments
 (0)