diff --git a/_todo/pending/graceful-no-files-exit.md b/_todo/pending/graceful-no-files-exit.md new file mode 100644 index 0000000..dd90087 --- /dev/null +++ b/_todo/pending/graceful-no-files-exit.md @@ -0,0 +1,111 @@ +# Task Proposal: Graceful Exit When No Files Found + +## Original Objective +When no .ipynb files (or .qmd files) are found to process, the CLI currently terminates with error code 1. This should be handled gracefully - just exit with code 0 since there's no work to do. + +## Current Behavior +The CLI exits with error code 1 in two scenarios: +1. **No files found initially** (quarto_batch_convert.py:301-303): When input paths contain no files with the target extension +2. **No files after regex filtering** (quarto_batch_convert.py:321-326): When regex pattern matches no files + +Both cases print error messages and call `ctx.exit(1)`. + +## Proposed Changes + +### 1. Exit with Code 0 for "No Work to Do" Scenarios +**File**: `src/quarto_batch_convert/quarto_batch_convert.py` + +**Change 1** (lines 301-303): +```python +# Before: +if not files: + click.echo("Error: No files found to process") + ctx.exit(1) + +# After: +if not files: + click.echo("No files found to process") + return # Exit gracefully with code 0 +``` + +**Change 2** (lines 321-326): +```python +# Before: +if not files: + if match_regex: + click.echo(f"No files found matching the regex pattern: {match_regex}") + else: + click.echo("No files found to process") + ctx.exit(1) + +# After: +if not files: + if match_regex: + click.echo(f"No files found matching the regex pattern: {match_regex}") + else: + click.echo("No files found to process") + return # Exit gracefully with code 0 +``` + +### 2. Update Tests (if needed) +**File**: `tests/test_quarto_batch_convert.py` + +Check if any tests expect exit code 1 for "no files found" scenarios. Update them to expect code 0 instead. + +## Implementation Steps + +1. Read `src/quarto_batch_convert/quarto_batch_convert.py` +2. Update lines 301-303: Remove "Error:" prefix, change `ctx.exit(1)` to `return` +3. Update lines 321-326: Change `ctx.exit(1)` to `return` +4. Read `tests/test_quarto_batch_convert.py` to check for affected tests +5. Update any tests that assert exit code 1 for "no files found" +6. Run test suite: `uv run pytest tests -v` +7. Verify locally with test case: run CLI on empty directory + +## Rationale + +- **Exit code 0**: No files to process is not an error condition - it's a valid state where the tool has nothing to do +- **Remove "Error:" prefix**: Since this isn't an error, the message shouldn't be labeled as such +- **Graceful termination**: Tools that find no work shouldn't fail CI/CD pipelines or automated scripts +- **Consistency**: Many Unix tools (grep, find, etc.) exit with 0 when finding no matches + +## Testing Scenarios + +1. Empty directory with `-r` flag +2. Directory with files but wrong extension +3. Regex pattern that matches no files +4. Non-existent glob pattern + +All should exit with code 0 and informative (non-error) message. + +## Implementation Summary + +**Status**: ✅ Completed + +### Changes Made + +1. **quarto_batch_convert.py:301-303** - Updated first exit point + - Removed "Error:" prefix from message + - Changed `ctx.exit(1)` to `return` for graceful exit + +2. **quarto_batch_convert.py:321-326** - Updated second exit point + - Changed `ctx.exit(1)` to `return` for graceful exit + - Kept informative message about regex pattern + +3. **test_quarto_batch_convert.py:128-145** - Updated test + - Changed `test_no_match_found()` to expect exit code 0 + - Updated docstring to reflect graceful exit behavior + +### Test Results +All 17 tests passed successfully: +- ✅ test_no_match_found now validates exit code 0 +- ✅ All other tests remain passing +- ✅ Test suite completed in 7.79s + +### Verification +The CLI now exits gracefully (code 0) when: +- No files with target extension are found in input paths +- Regex pattern matches no files +- Directory is empty + +Non-error scenarios no longer fail CI/CD pipelines or automated scripts. diff --git a/_todo/todo.md b/_todo/todo.md index 37dd5ae..78dff41 100644 --- a/_todo/todo.md +++ b/_todo/todo.md @@ -2,7 +2,7 @@ ## Active Tasks - +1. **graceful-no-files-exit** - In progress in `pending/graceful-no-files-exit.md` ## Proposed Tasks (Awaiting Review) diff --git a/pyproject.toml b/pyproject.toml index 78154d2..ce33f6a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "quarto-batch-convert" -version = "2025.9.1" +version = "2025.9.2" description = "Converts multiple Jupyter notebooks to Quarto documents at once" readme = "README.md" license = "MIT" diff --git a/src/quarto_batch_convert/quarto_batch_convert.py b/src/quarto_batch_convert/quarto_batch_convert.py index 7a6119f..0816b4e 100644 --- a/src/quarto_batch_convert/quarto_batch_convert.py +++ b/src/quarto_batch_convert/quarto_batch_convert.py @@ -299,8 +299,8 @@ def convert_files( # Check if any files were found if not files: - click.echo("Error: No files found to process") - ctx.exit(1) + click.echo("No files found to process") + return if output_path is None: output_path = base_input_path @@ -323,7 +323,7 @@ def convert_files( click.echo(f"No files found matching the regex pattern: {match_regex}") else: click.echo("No files found to process") - ctx.exit(1) + return print(f"Found {len(files)} file(s) to be converted:\n") print(f"\n\t{files}\n") diff --git a/tests/test_quarto_batch_convert.py b/tests/test_quarto_batch_convert.py index 58c351d..7fe917e 100644 --- a/tests/test_quarto_batch_convert.py +++ b/tests/test_quarto_batch_convert.py @@ -128,20 +128,20 @@ def test_invalid_regex_pattern() -> None: def test_no_match_found(setup_teardown_test_env: str) -> None: """Test that a non-matching pattern results in no files being processed. - Verifies that when a regex pattern matches no files, the CLI exits with - an error code and displays an appropriate message. + Verifies that when a regex pattern matches no files, the CLI exits gracefully + with exit code 0 and displays an appropriate message. Args: setup_teardown_test_env: Path to the temporary test directory. """ runner = CliRunner() test_dir = setup_teardown_test_env - + input_files = glob.glob(test_dir + "/**/*", recursive=True) - + result = runner.invoke(quarto_batch_convert, [*input_files, "-m", "non_existent_pattern"]) - - assert result.exit_code != 0 + + assert result.exit_code == 0 assert "No files found matching the regex pattern" in result.output def test_prefix_option(setup_teardown_test_env: str) -> None: diff --git a/uv.lock b/uv.lock index da994a5..38df720 100644 --- a/uv.lock +++ b/uv.lock @@ -81,7 +81,7 @@ wheels = [ [[package]] name = "quarto-batch-convert" -version = "2025.9.1" +version = "2025.9.2" source = { editable = "." } dependencies = [ { name = "click" },