Skip to content

Commit 1d9fd76

Browse files
authored
Add custom exception class for CLI (#1919)
* Add custom exception class for CLI * Fixed TCs for config_manager --------- Signed-off-by: Yunchu Lee <[email protected]>
1 parent 074d6af commit 1d9fd76

File tree

5 files changed

+60
-13
lines changed

5 files changed

+60
-13
lines changed

otx/cli/manager/config_manager.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
from otx.api.entities.model_template import ModelTemplate, parse_model_template
1717
from otx.cli.registry import Registry as OTXRegistry
1818
from otx.cli.utils.config import configure_dataset, override_parameters
19+
from otx.cli.utils.errors import (
20+
CliException,
21+
ConfigValueError,
22+
FileNotExistError,
23+
NotSupportedError,
24+
)
1925
from otx.cli.utils.importing import get_otx_root_path
2026
from otx.cli.utils.parser import gen_param_help, gen_params_dict_from_args
2127
from otx.core.data.manager.dataset_manager import DatasetManager
@@ -112,7 +118,7 @@ def data_config_file_path(self) -> Path:
112118
if "data" in self.args and self.args.data:
113119
if Path(self.args.data).exists():
114120
return Path(self.args.data)
115-
raise FileNotFoundError(f"Not found: {self.args.data}")
121+
raise FileNotExistError(f"Not found: {self.args.data}")
116122
return self.workspace_root / "data.yaml"
117123

118124
def check_workspace(self) -> bool:
@@ -140,6 +146,8 @@ def configure_template(self, model: str = None) -> None:
140146
else:
141147
task_type = self.task_type
142148
if not task_type and not model:
149+
if not hasattr(self.args, "train_data_roots"):
150+
raise ConfigValueError("Can't find the argument 'train_data_roots'")
143151
task_type = self.auto_task_detection(self.args.train_data_roots)
144152
self.template = self._get_template(task_type, model=model)
145153
self.task_type = self.template.task_type
@@ -149,7 +157,7 @@ def configure_template(self, model: str = None) -> None:
149157
def _check_rebuild(self):
150158
"""Checking for Rebuild status."""
151159
if self.args.task and str(self.template.task_type) != self.args.task.upper():
152-
raise NotImplementedError("Task Update is not yet supported.")
160+
raise NotSupportedError("Task Update is not yet supported.")
153161
result = False
154162
if self.args.model and self.template.name != self.args.model.upper():
155163
print(f"[*] Rebuild model: {self.template.name} -> {self.args.model.upper()}")
@@ -189,7 +197,7 @@ def _get_train_type(self, ignore_args: bool = False) -> str:
189197
if hasattr(self.args, "train_type") and self.mode in ("build", "train") and self.args.train_type:
190198
self.train_type = self.args.train_type.upper()
191199
if self.train_type not in TASK_TYPE_TO_SUB_DIR_NAME:
192-
raise ValueError(f"{self.train_type} is not currently supported by otx.")
200+
raise NotSupportedError(f"{self.train_type} is not currently supported by otx.")
193201
if self.train_type in TASK_TYPE_TO_SUB_DIR_NAME:
194202
return self.train_type
195203

@@ -202,7 +210,7 @@ def _get_train_type(self, ignore_args: bool = False) -> str:
202210
def auto_task_detection(self, data_roots: str) -> str:
203211
"""Detect task type automatically."""
204212
if not data_roots:
205-
raise ValueError("Workspace must already exist or one of {task or model or train-data-roots} must exist.")
213+
raise CliException("Workspace must already exist or one of {task or model or train-data-roots} must exist.")
206214
self.data_format = self.dataset_manager.get_data_format(data_roots)
207215
return self._get_task_type_from_data_format(self.data_format)
208216

@@ -225,7 +233,7 @@ def _get_task_type_from_data_format(self, data_format: str) -> str:
225233
self.task_type = task_key
226234
print(f"[*] Detected task type: {self.task_type}")
227235
return task_key
228-
raise ValueError(f"Can't find proper task. we are not support {data_format} format, yet.")
236+
raise ConfigValueError(f"Can't find proper task. we are not support {data_format} format, yet.")
229237

230238
def auto_split_data(self, data_roots: str, task: str):
231239
"""Automatically Split train data --> train/val dataset."""
@@ -372,7 +380,7 @@ def _get_template(self, task_type: str, model: Optional[str] = None) -> ModelTem
372380
if model:
373381
template_lst = [temp for temp in otx_registry.templates if temp.name.lower() == model.lower()]
374382
if not template_lst:
375-
raise ValueError(
383+
raise NotSupportedError(
376384
f"[*] {model} is not a type supported by OTX {task_type}."
377385
f"\n[*] Please refer to 'otx find --template --task {task_type}'"
378386
)
@@ -426,7 +434,7 @@ def build_workspace(self, new_workspace_path: Optional[str] = None) -> None:
426434

427435
model_dir = template_dir.absolute() / train_type_rel_path
428436
if not model_dir.exists():
429-
raise ValueError(f"[*] {self.train_type} is not a type supported by OTX {self.task_type}")
437+
raise NotSupportedError(f"[*] {self.train_type} is not a type supported by OTX {self.task_type}")
430438
train_type_dir = self.workspace_root / train_type_rel_path
431439
train_type_dir.mkdir(exist_ok=True)
432440

@@ -479,7 +487,7 @@ def _copy_config_files(self, target_dir: Path, file_name: str, dest_dir: Path) -
479487
config = MPAConfig.fromfile(str(target_dir / file_name))
480488
config.dump(str(dest_dir / file_name))
481489
except Exception as exc:
482-
raise ImportError(f"{self.task_type} requires mmcv-full to be installed.") from exc
490+
raise CliException(f"{self.task_type} requires mmcv-full to be installed.") from exc
483491
elif file_name.endswith((".yml", ".yaml")):
484492
config = OmegaConf.load(str(target_dir / file_name))
485493
(dest_dir / file_name).write_text(OmegaConf.to_yaml(config))

otx/cli/utils/errors.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Utils for CLI errors."""
2+
3+
# Copyright (C) 2023 Intel Corporation
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
8+
class CliException(Exception):
9+
"""Custom exception class for CLI."""
10+
11+
12+
class ConfigValueError(CliException):
13+
"""Configuration value is not suitable for CLI."""
14+
15+
def __init__(self, message):
16+
super().__init__(message)
17+
18+
19+
class NotSupportedError(CliException):
20+
"""Not supported error."""
21+
22+
def __init__(self, message):
23+
super().__init__(message)
24+
25+
26+
class FileNotExistError(CliException):
27+
"""Not exist given configuration."""
28+
29+
def __init__(self, message):
30+
super().__init__(message)

tests/fuzzing/cli_fuzzing.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from helper import FuzzingHelper
55

66
from otx.cli.tools.cli import main as cli_main
7+
from otx.cli.utils.errors import CliException
78

89

910
@atheris.instrument_func
@@ -21,6 +22,8 @@ def fuzz_otx(input_bytes):
2122
# argparser will throw SystemExit with code 2 when some required arguments are missing
2223
if e.code != 2:
2324
raise
25+
except CliException:
26+
pass
2427
# some known exceptions can be catched here
2528
finally:
2629
sys.argv = backup_argv

tests/unit/cli/manager/test_config_manager.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
set_workspace,
1111
)
1212
from otx.cli.registry import Registry
13+
from otx.cli.utils.errors import (
14+
CliException,
15+
ConfigValueError,
16+
FileNotExistError,
17+
NotSupportedError,
18+
)
1319
from tests.test_suite.e2e_test_system import e2e_pytest_unit
1420

1521

@@ -319,7 +325,7 @@ def test_data_config_file_path(self, mocker, tmp_dir_path):
319325
expected_file_path = tmp_dir_path / "data.yaml"
320326
args = parser.parse_args(["--data", str(expected_file_path)])
321327
config_manager.args = args
322-
with pytest.raises(FileNotFoundError):
328+
with pytest.raises(FileNotExistError):
323329
config_manager.data_config_file_path
324330

325331
mock_exists.return_value = True
@@ -389,7 +395,7 @@ def test__check_rebuild(self, mocker):
389395
mock_args.template = mock_template
390396

391397
config_manager = ConfigManager(mock_args)
392-
with pytest.raises(NotImplementedError):
398+
with pytest.raises(NotSupportedError):
393399
config_manager._check_rebuild()
394400

395401
config_manager.template.task_type = "DETECTION"
@@ -466,13 +472,13 @@ def test__get_train_type(self, mocker):
466472
def test_auto_task_detection(self, mocker):
467473
mock_args = mocker.MagicMock()
468474
config_manager = ConfigManager(args=mock_args)
469-
with pytest.raises(ValueError):
475+
with pytest.raises(CliException):
470476
config_manager.auto_task_detection("")
471477

472478
mock_get_data_format = mocker.patch(
473479
"otx.cli.manager.config_manager.DatasetManager.get_data_format", return_value="Unexpected"
474480
)
475-
with pytest.raises(ValueError):
481+
with pytest.raises(ConfigValueError):
476482
config_manager.auto_task_detection("data/roots")
477483

478484
mock_get_data_format.return_value = "coco"

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ deps =
147147
use_develop = true
148148
commands =
149149
coverage erase
150-
- coverage run tests/fuzzing/cli_fuzzing.py {posargs:-dict=tests/fuzzing/assets/cli/operations.dict -artifact_prefix={toxworkdir}/ -print_final_stats=1 -atheris_runs=100000}
150+
- coverage run tests/fuzzing/cli_fuzzing.py {posargs:-dict=tests/fuzzing/assets/cli/operations.dict -artifact_prefix={toxworkdir}/ -print_final_stats=1 -atheris_runs=500000}
151151
coverage report --precision=2
152152
; coverage html -d {toxworkdir}/htmlcov
153153

0 commit comments

Comments
 (0)