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
111 changes: 111 additions & 0 deletions _todo/pending/graceful-no-files-exit.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion _todo/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Active Tasks

<!-- Add tasks here. When creating a proposal, move the task description to the proposal file -->
1. **graceful-no-files-exit** - In progress in `pending/graceful-no-files-exit.md`

## Proposed Tasks (Awaiting Review)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
6 changes: 3 additions & 3 deletions src/quarto_batch_convert/quarto_batch_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand Down
12 changes: 6 additions & 6 deletions tests/test_quarto_batch_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.