mirror of
https://github.com/NVIDIA-NeMo/DataDesigner
synced 2026-05-24 09:48:29 +00:00
* feat: add preview, create, and validate CLI commands Add three new top-level CLI commands for the data-designer workflow: - `data-designer preview` - generate preview datasets for fast iteration - `data-designer create` - create full datasets and save to disk - `data-designer validate` - validate configuration files Also includes: - Move wait_for_navigation_key() UI primitive from preview.py to ui.py - Add KeyPressEvent type annotations to all key binding handlers in ui.py - Refactor cli/utils.py into cli/utils/ package with config_loader module - Comprehensive test coverage for all new commands * fix: update pythonjsonlogger import and clean up dev dependencies - Update pythonjsonlogger import to use newer JsonFormatter API - Consolidate dev-dependencies into [dependency-groups] dev section - Remove unnecessary test cli/utils __init__.py * small E * address greptile feedback * organize CLI commands into rich help panels Group top-level commands under "Generation" and "Setup" panels for clearer help output. * refactor config loader to parse files directly and auto-detect config format - Parse YAML/JSON files into dicts before passing to from_config, providing format-specific error messages for parse failures - Auto-detect DataDesignerConfig format (columns at top level) and wrap it into BuilderConfig so users can provide either format - Clean up Python module loading with try/except/finally for reliable sys.modules and sys.path cleanup - Add comprehensive tests for parsing, validation, and auto-wrapping * fix sys.path cleanup in config loader and simplify tests - Use pop(0) instead of remove() to precisely undo the insert(0, ...) and avoid accidentally removing a different matching path entry - Replace MagicMock with real DataDesignerConfigBuilder in tests * move config format auto-detection into from_config Centralize the shorthand DataDesignerConfig detection (columns at top level without a data_designer wrapper) in DataDesignerConfigBuilder.from_config so all callers benefit, not just the CLI config loader. Simplify config_loader to delegate file parsing and format normalization entirely to from_config. * extract GenerationController from CLI commands Move shared generation logic (preview, validate, create) out of the individual Typer command functions into a dedicated GenerationController, matching the existing controller pattern (DownloadController, etc.). The command functions now delegate to the controller, keeping them as thin entry points. Tests updated accordingly — command tests verify delegation while controller tests cover the full behavior. * harden sys.path cleanup and add explanatory comments Use sys.path.remove() instead of checking sys.path[0] so cleanup succeeds even when exec_module inserts entries at index 0. Drop unnecessary spec=DataDesignerConfigBuilder from test mocks. * check stdout TTY in preview interactive mode detection Previously only stdin was checked, so piping stdout (e.g. `dd preview cfg.yaml | head`) would still attempt interactive browsing. Now both stdin and stdout must be a TTY.
66 lines
2.3 KiB
Python
66 lines
2.3 KiB
Python
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from data_designer.cli.commands.create import create_command
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# create_command delegation tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@patch("data_designer.cli.commands.create.GenerationController")
|
|
def test_create_command_delegates_to_controller(mock_ctrl_cls: MagicMock) -> None:
|
|
"""Test create_command delegates to GenerationController.run_create."""
|
|
mock_ctrl = MagicMock()
|
|
mock_ctrl_cls.return_value = mock_ctrl
|
|
|
|
create_command(config_source="config.yaml", num_records=10, dataset_name="dataset", artifact_path=None)
|
|
|
|
mock_ctrl_cls.assert_called_once()
|
|
mock_ctrl.run_create.assert_called_once_with(
|
|
config_source="config.yaml",
|
|
num_records=10,
|
|
dataset_name="dataset",
|
|
artifact_path=None,
|
|
)
|
|
|
|
|
|
@patch("data_designer.cli.commands.create.GenerationController")
|
|
def test_create_command_passes_custom_options(mock_ctrl_cls: MagicMock) -> None:
|
|
"""Test create_command passes custom options to the controller."""
|
|
mock_ctrl = MagicMock()
|
|
mock_ctrl_cls.return_value = mock_ctrl
|
|
|
|
create_command(
|
|
config_source="config.py",
|
|
num_records=100,
|
|
dataset_name="my_data",
|
|
artifact_path="/custom/output",
|
|
)
|
|
|
|
mock_ctrl.run_create.assert_called_once_with(
|
|
config_source="config.py",
|
|
num_records=100,
|
|
dataset_name="my_data",
|
|
artifact_path="/custom/output",
|
|
)
|
|
|
|
|
|
@patch("data_designer.cli.commands.create.GenerationController")
|
|
def test_create_command_default_artifact_path_is_none(mock_ctrl_cls: MagicMock) -> None:
|
|
"""Test create_command passes artifact_path=None when not specified."""
|
|
mock_ctrl = MagicMock()
|
|
mock_ctrl_cls.return_value = mock_ctrl
|
|
|
|
create_command(config_source="config.yaml", num_records=5, dataset_name="ds", artifact_path=None)
|
|
|
|
mock_ctrl.run_create.assert_called_once_with(
|
|
config_source="config.yaml",
|
|
num_records=5,
|
|
dataset_name="ds",
|
|
artifact_path=None,
|
|
)
|