Skip to content

Commit 0ed25b8

Browse files
authored
Merge pull request #1184 from VisLab/add_examples
Resolved #1183 and continue work on scripts
2 parents 019d745 + 8d17b81 commit 0ed25b8

21 files changed

+991
-158
lines changed

hed/cli/cli.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,11 @@ def validate():
186186
is_flag=True,
187187
help="Suppress log output to stderr; only applicable when --log-file is used (logs go only to file)",
188188
)
189+
@optgroup.option(
190+
"--no-log",
191+
is_flag=True,
192+
help="Disable all logging output",
193+
)
189194
def validate_bids_cmd(
190195
data_path,
191196
error_limit,
@@ -194,6 +199,7 @@ def validate_bids_cmd(
194199
log_level,
195200
log_file,
196201
log_quiet,
202+
no_log,
197203
output_file,
198204
print_output,
199205
suffixes,
@@ -221,6 +227,8 @@ def validate_bids_cmd(
221227
args.extend(["-lf", log_file])
222228
if log_quiet:
223229
args.append("-lq")
230+
if no_log:
231+
args.append("--no-log")
224232
if output_file:
225233
args.extend(["-o", output_file])
226234
if print_output:
@@ -239,6 +247,157 @@ def validate_bids_cmd(
239247
validate_bids_main(args)
240248

241249

250+
@validate.command(
251+
name="hed-string",
252+
epilog="""
253+
This command validates a HED annotation string against a specified HED schema
254+
version. It can optionally process definitions and check for warnings in addition
255+
to errors. Multiple schema versions can be specified for validation with library schemas.
256+
257+
\b
258+
Examples:
259+
# Basic validation of a HED string
260+
hedpy validate hed-string "Event, (Sensory-event, (Visual-presentation, (Computer-screen, Face)))" -sv 8.3.0
261+
262+
# Validate with definitions
263+
hedpy validate hed-string "Event, Def/MyDef" -sv 8.4.0 -d "(Definition/MyDef, (Action, Move))"
264+
265+
# Validate with multiple schemas (base + library)
266+
hedpy validate hed-string "Event, Action" -sv 8.3.0 -sv score_1.1.0
267+
268+
# Check for warnings as well as errors
269+
hedpy validate hed-string "Event, Action/Button-press" -sv 8.4.0 --check-for-warnings
270+
271+
# Save validation results to a file
272+
hedpy validate hed-string "Event" -sv 8.4.0 -o validation_results.txt
273+
274+
# Output results in JSON format
275+
hedpy validate hed-string "Event, Action" -sv 8.4.0 -f json
276+
277+
# Verbose output with informational messages
278+
hedpy validate hed-string "Event, (Action, Move)" -sv 8.4.0 --verbose
279+
""",
280+
)
281+
@click.argument("hed_string")
282+
# Validation options
283+
@optgroup.group("Validation options")
284+
@optgroup.option(
285+
"-sv",
286+
"--schema-version",
287+
required=True,
288+
multiple=True,
289+
metavar="VERSION",
290+
help="HED schema version(s) to validate against (e.g., '8.3.0'). Can be specified multiple times for multiple schemas (e.g., -sv 8.3.0 -sv score_1.1.0)",
291+
)
292+
@optgroup.option(
293+
"-d",
294+
"--definitions",
295+
default="",
296+
metavar=METAVAR_STRING,
297+
help="A string containing relevant HED definitions to use during validation (e.g., '(Definition/MyDef, (Action, Move))')",
298+
)
299+
@optgroup.option(
300+
"-w",
301+
"--check-for-warnings",
302+
is_flag=True,
303+
help="Check for warnings as well as errors",
304+
)
305+
# Output options
306+
@optgroup.group("Output options")
307+
@optgroup.option(
308+
"-f",
309+
"--format",
310+
type=click.Choice(["text", "json"]),
311+
default="text",
312+
show_default="text",
313+
help="Output format for validation results (text: human-readable; json: structured format for programmatic use)",
314+
)
315+
@optgroup.option(
316+
"-o",
317+
"--output-file",
318+
type=click.Path(),
319+
default="",
320+
metavar=METAVAR_FILE,
321+
help="Path for output file to hold validation results; if not specified, output to stdout",
322+
)
323+
# Logging options
324+
@optgroup.group("Logging options")
325+
@optgroup.option(
326+
"-l",
327+
"--log-level",
328+
type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]),
329+
default="WARNING",
330+
show_default="WARNING",
331+
help="Log level for diagnostic messages",
332+
)
333+
@optgroup.option(
334+
"-v",
335+
"--verbose",
336+
is_flag=True,
337+
help="Output informational messages (equivalent to --log-level INFO)",
338+
)
339+
@optgroup.option(
340+
"-lf",
341+
"--log-file",
342+
type=click.Path(),
343+
metavar=METAVAR_FILE,
344+
help="File path for saving log output; logs still go to stderr unless --log-quiet is also used",
345+
)
346+
@optgroup.option(
347+
"-lq",
348+
"--log-quiet",
349+
is_flag=True,
350+
help="Suppress log output to stderr; only applicable when --log-file is used (logs go only to file)",
351+
)
352+
@optgroup.option(
353+
"--no-log",
354+
is_flag=True,
355+
help="Disable all logging output",
356+
)
357+
def validate_hed_string_cmd(
358+
hed_string,
359+
schema_version,
360+
definitions,
361+
check_for_warnings,
362+
format,
363+
output_file,
364+
log_level,
365+
log_file,
366+
log_quiet,
367+
no_log,
368+
verbose,
369+
):
370+
"""Validate a HED annotation string.
371+
372+
HED_STRING: The HED annotation string to validate (use quotes for strings with spaces or special characters).
373+
"""
374+
from hed.scripts.validate_hed_string import main as validate_string_main
375+
376+
args = [hed_string]
377+
for version in schema_version:
378+
args.extend(["-sv", version])
379+
if definitions:
380+
args.extend(["-d", definitions])
381+
if check_for_warnings:
382+
args.append("-w")
383+
if format:
384+
args.extend(["-f", format])
385+
if output_file:
386+
args.extend(["-o", output_file])
387+
if log_level:
388+
args.extend(["-l", log_level])
389+
if log_file:
390+
args.extend(["-lf", log_file])
391+
if log_quiet:
392+
args.append("-lq")
393+
if no_log:
394+
args.append("--no-log")
395+
if verbose:
396+
args.append("-v")
397+
398+
validate_string_main(args)
399+
400+
242401
@schema.command(name="validate")
243402
@click.argument("schema_path", type=click.Path(exists=True), nargs=-1, required=True)
244403
@click.option("--add-all-extensions", is_flag=True, help="Always verify all versions of the same schema are equal")

hed/errors/__init__.py

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

3-
from .error_reporter import ErrorHandler, get_printable_issue_string, sort_issues, replace_tag_references
3+
from .error_reporter import ErrorHandler, get_printable_issue_string, sort_issues, replace_tag_references, iter_errors
44
from .error_types import (
55
DefinitionErrors,
66
TemporalErrors,

hed/schema/hed_cache_lock.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import os
55
import portalocker
66

7-
87
TIMESTAMP_FILENAME = "last_update.txt"
98
CACHE_TIME_THRESHOLD = 300 * 6
109

hed/schema/schema_io/wiki2schema.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from hed.schema.schema_io.wiki_constants import HedWikiSection, WIKI_EXTRA_DICT
1414
from hed.schema.schema_io import text_util
1515

16-
1716
extend_here_line = "extend here"
1817
invalid_characters_to_strip = ["​"]
1918
tag_name_expression = r"(\*+|\'{3})(.*?)(\'{3})?\s*([\[\{]|$)+"

hed/schema/schema_validation_util_deprecated.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from hed.errors.error_reporter import ErrorHandler
44
from hed.errors.error_types import SchemaWarnings
55

6-
76
ALLOWED_TAG_CHARS = "-"
87
ALLOWED_DESC_CHARS = "-_:;,./()+ ^"
98

hed/scripts/script_utils.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"""
2+
Utility functions for HED command-line scripts.
3+
4+
This module provides common functionality used across multiple HED scripts,
5+
including logging configuration and argument handling.
6+
"""
7+
8+
import json
9+
import logging
10+
import sys
11+
from hed import _version as vr
12+
from hed.errors import get_printable_issue_string, ErrorHandler, iter_errors
13+
14+
15+
def setup_logging(log_level, log_file=None, log_quiet=False, verbose=False, no_log=False):
16+
"""Configure logging for HED scripts.
17+
18+
Sets up the root logger with appropriate handlers for console (stderr) and/or
19+
file output based on the provided arguments.
20+
21+
Parameters:
22+
log_level (str): Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
23+
log_file (str or None): Path to log file, or None for no file logging
24+
log_quiet (bool): If True and log_file is specified, suppress stderr output
25+
verbose (bool): If True, override log_level to INFO
26+
no_log (bool): If True, disable all logging output
27+
28+
Returns:
29+
logging.Logger: Configured logger instance
30+
"""
31+
# Disable logging completely if requested
32+
if no_log:
33+
logging.basicConfig(level=logging.CRITICAL + 1, handlers=[logging.NullHandler()], force=True)
34+
return logging.getLogger()
35+
36+
# Determine effective log level
37+
level = logging.INFO if verbose else getattr(logging, log_level.upper())
38+
39+
# Configure handlers
40+
handlers = []
41+
if log_file:
42+
handlers.append(logging.FileHandler(log_file, mode="w", encoding="utf-8"))
43+
if not (log_file and log_quiet):
44+
handlers.append(logging.StreamHandler(sys.stderr))
45+
46+
# Configure root logger
47+
logging.basicConfig(level=level, format="%(levelname)s: %(message)s", handlers=handlers, force=True)
48+
49+
return logging.getLogger()
50+
51+
52+
def format_validation_results(
53+
issue_list, output_format="text", title_message="Validation errors:", error_limit=None, errors_by_file=False
54+
):
55+
"""Format validation results in the requested output format.
56+
57+
This function provides a consistent way to format validation issues across
58+
different HED validation scripts. It supports text, JSON, and pretty-printed
59+
JSON formats, with optional error limiting for large result sets.
60+
61+
Parameters:
62+
issue_list (list): List of validation issues (HedIssue objects)
63+
output_format (str): Output format - 'text', 'json', or 'json_pp' (default: 'text')
64+
title_message (str): Title/header for text output (default: 'Validation errors:')
65+
error_limit (int or None): Maximum errors per code type to include in text output (default: None)
66+
errors_by_file (bool): Apply error limit per file rather than globally (default: False)
67+
68+
Returns:
69+
str: Formatted validation results as a string
70+
71+
Examples:
72+
>>> issues = validator.validate(hed_string)
73+
>>> output = format_validation_results(issues, "text", "HED string validation:")
74+
>>> output = format_validation_results(issues, "json")
75+
>>> output = format_validation_results(issues, "json_pp")
76+
"""
77+
if output_format == "json_pp":
78+
# Pretty-printed JSON with version metadata
79+
# Convert issues to JSON-serializable format
80+
serializable_issues = list(iter_errors(issue_list))
81+
return json.dumps({"issues": serializable_issues, "hedtools_version": str(vr.get_versions())}, indent=4)
82+
83+
elif output_format == "json":
84+
# Compact JSON array of issues
85+
# Convert issues to JSON-serializable format
86+
serializable_issues = list(iter_errors(issue_list))
87+
return json.dumps(serializable_issues)
88+
89+
elif output_format == "text":
90+
# Human-readable text format with counts and optional filtering
91+
output = f"Using HEDTools version: {str(vr.get_versions())}\n"
92+
output += f"Number of issues: {len(issue_list)}\n"
93+
94+
# Apply error limiting if requested
95+
if error_limit:
96+
filtered_issues, code_counts = ErrorHandler.filter_issues_by_count(issue_list, error_limit, by_file=errors_by_file)
97+
output += "Error counts by code: "
98+
output += " ".join(f"{code}:{count}" for code, count in code_counts.items()) + "\n"
99+
output += f"Number of issues after filtering: {len(filtered_issues)}\n"
100+
issue_list = filtered_issues
101+
102+
# Format the issues with title
103+
if issue_list:
104+
output += get_printable_issue_string(issue_list, title_message, skip_filename=False)
105+
106+
return output
107+
108+
else:
109+
raise ValueError(f"Unknown output format: {output_format}")

0 commit comments

Comments
 (0)