Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions negate/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from typing import Any

from negate.io.console import CLI_LOGGER, set_root_folder
from negate.io.blurb import BlurbText
from negate.extract.unified_core import ExtractionModule

ROOT_FOLDER = Path(__file__).resolve().parent.parent
CONFIG_PATH = ROOT_FOLDER / "config"
Expand Down Expand Up @@ -106,7 +108,9 @@ def _load_model_choices() -> ModelChoices:
return choices


def _build_parser(blurb: BlurbText, choices: ModelChoices, list_results: list[str], list_model: list[str], inference_pair: list[str]) -> argparse.ArgumentParser:
def _build_parser(
blurb: BlurbText, choices: ModelChoices, list_results: list[str], list_model: list[str], inference_pair: list[str]
) -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Negate CLI")
subparsers = parser.add_subparsers(dest="cmd", required=True)

Expand All @@ -115,11 +119,13 @@ def _build_parser(blurb: BlurbText, choices: ModelChoices, list_results: list[st
train_parser.add_argument("-l", "--loop", action="store_true", help=blurb.loop)
train_parser.add_argument("-f", "--features", choices=list_results, default=None, help=blurb.features_load)

process_parser = subparsers.add_parser("process", help="Run all decompose/extract module combinations")
module_list = ", ".join(mod.name for mod in ExtractionModule)
process_help = f"Run all decompose/extract module combinations. Available modules: {module_list}"
process_parser = subparsers.add_parser("process", help=process_help)
process_parser.add_argument("path", help=blurb.unidentified_path)
process_parser.add_argument("-v", "--verbose", action="store_true", help=blurb.verbose)
process_parser.add_argument("--transposed", default=None, help="Comma-separated transposed indices")
process_parser.add_argument("--combination", default=None, help="Comma-separated module names")
process_parser.add_argument("--combination", default=None, help=f"Comma-separated module names from: {module_list}")
process_parser.add_argument("--train", choices=["convnext", "xgboost"], default=None, help="Train model after processing")

vit_help = f"Vison {blurb.model_desc} {choices.default_vit}".strip()
Expand Down
1 change: 1 addition & 0 deletions negate/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from pathlib import Path
from typing import Any
import time as timer_module
from negate.io.console import configure_runtime_logging, CLI_LOGGER

start_ns = timer_module.perf_counter()

Expand Down
3 changes: 3 additions & 0 deletions results/20260412_080531/results_real_20260412_080531.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"x_p": "{'image_mean_ff': 0.5551752934617227, 'image_std': 0.0977445244532872, 'image_mean': (28894107043657, 57788214087314), 'diff_mean': (25353770906193, 50707541812387), 'laplace_mean': (24083985868529, 48167971737058), 'sobel_mean': (26436775610593, 52873551221187), 'image_tc': (16, 16), 'diff_tc': (29, 29), 'laplace_tc': (25, 25), 'sobel_tc': (25, 25), 'spectral_tc': (16, 16)}"
}
77 changes: 77 additions & 0 deletions tests/test_main_help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# SPDX-License-Identifier: MPL-2.0 AND LicenseRef-Commons-Clause-License-Condition-1.0
# <!-- // /* d a r k s h a p e s */ -->

"""Test suite for CLI help text."""

import argparse
import sys
from io import StringIO
from negate.extract.unified_core import ExtractionModule


def test_process_help_includes_all_modules():
"""Verify process command help lists all ExtractionModule options."""
from negate.__main__ import _load_blurb_text, _load_model_choices, _build_parser

blurb = _load_blurb_text()
choices = _load_model_choices()

parser = _build_parser(
blurb=blurb,
choices=choices,
list_results=[],
list_model=[],
inference_pair=[],
)

help_output = StringIO()
try:
parser.parse_args(["process", "-h"])
except SystemExit:
pass

help_text = parser.format_help()
process_subparser = None

for action in parser._subparsers._actions:
if isinstance(action, argparse._SubParsersAction):
if "process" in action.choices:
process_subparser = action.choices["process"]
break

assert process_subparser is not None, "process subparser not found"

process_help = process_subparser.format_help()

for module in ExtractionModule:
assert module.name in process_help, f"Module {module.name} not found in process help text"


def test_process_help_module_list_matches_enum():
"""Verify the module list in help matches ExtractionModule enum."""
from negate.__main__ import _load_blurb_text, _load_model_choices, _build_parser

blurb = _load_blurb_text()
choices = _load_model_choices()

parser = _build_parser(
blurb=blurb,
choices=choices,
list_results=[],
list_model=[],
inference_pair=[],
)

for action in parser._subparsers._actions:
if isinstance(action, argparse._SubParsersAction):
if "process" in action.choices:
process_subparser = action.choices["process"]
break

process_help = process_subparser.format_help()

module_names = [mod.name for mod in ExtractionModule]
for module_name in module_names:
assert module_name in process_help, f"Module {module_name} missing from help"

assert len(module_names) == len(ExtractionModule), "Module list count mismatch"
Loading