Skip to content

fix: guard _read_line_nonblocking against sys.stdin is None (#1170)#1172

Open
microsasa wants to merge 1 commit intomainfrom
fix/guard-stdin-none-1170-182febf1f554f4fa
Open

fix: guard _read_line_nonblocking against sys.stdin is None (#1170)#1172
microsasa wants to merge 1 commit intomainfrom
fix/guard-stdin-none-1170-182febf1f554f4fa

Conversation

@microsasa
Copy link
Copy Markdown
Owner

Closes #1170

Problem

When sys.stdin is None (e.g. process started with closed stdin, or certain test/CI environments), select.select([None], [], [], timeout) raises a TypeError that isn't caught by the except (ValueError, OSError) handler in _interactive_loop. This crashes the interactive session instead of falling back to the threaded reader.

Fix

Added a sys.stdin is None guard at the top of _read_line_nonblocking that raises ValueError("stdin is None"), which the existing except (ValueError, OSError) handler in _interactive_loop already catches and handles via the threaded fallback.

Changes

  1. src/copilot_usage/cli.py — Added if sys.stdin is None: raise ValueError("stdin is None") guard and updated docstring to document sys.stdin is None as a trigger condition
  2. src/copilot_usage/docs/implementation.md — Updated fallback documentation to include sys.stdin is None as a third triggering case
  3. tests/copilot_usage/test_cli.py — Added:
    • test_none_stdin_raises_value_error in TestReadLineNonblocking class (unit test)
    • test_interactive_loop_none_stdin_falls_back_to_input in Gap 3 section (integration test)

Warning

⚠️ Firewall blocked 3 domains

The following domains were blocked by the firewall during workflow execution:

  • astral.sh
  • pypi.org
  • releaseassets.githubusercontent.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "astral.sh"
    - "pypi.org"
    - "releaseassets.githubusercontent.com"

See Network Configuration for more information.

Generated by Issue Implementer · ● 14.4M ·

Add a None check at the top of _read_line_nonblocking that raises
ValueError (already caught by _interactive_loop) before select.select
can raise an uncaught TypeError.

- Update docstring to mention sys.stdin is None as a trigger
- Update implementation.md fallback documentation
- Add unit test for the ValueError raise
- Add integration test for _interactive_loop fallback path

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 3, 2026 10:09
@microsasa microsasa added the aw Created by agentic workflow label May 3, 2026
@microsasa microsasa enabled auto-merge May 3, 2026 10:09
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the interactive CLI input path so _interactive_loop() no longer crashes when sys.stdin is None, which can happen in closed-stdin or CI/test environments. It fits the CLI’s existing fallback design by converting that condition into the same exception path already used for non-selectable stdin.

Changes:

  • Added a guard in _read_line_nonblocking() to raise ValueError when sys.stdin is None.
  • Added unit and interactive-loop tests covering the new stdin-none path.
  • Updated implementation docs to mention sys.stdin is None as a fallback trigger.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/copilot_usage/cli.py Adds the stdin-none guard in the nonblocking reader docstring and implementation.
src/copilot_usage/docs/implementation.md Updates the interactive-loop design docs for the new fallback trigger.
tests/copilot_usage/test_cli.py Adds regression tests for _read_line_nonblocking() and interactive fallback behavior.

### Fallback to threaded `_start_input_reader_thread()`

If `select()` raises `ValueError` or `OSError` (e.g. stdin is not selectable, notably on Windows, or stdin is detached during testing), the loop starts a daemon thread via `_start_input_reader_thread()` (in `cli.py`) that feeds lines into a `queue.SimpleQueue`:
If `select()` raises `ValueError` or `OSError` (e.g. stdin is not selectable, notably on Windows, stdin is detached during testing, or `sys.stdin is None`), the loop starts a daemon thread via `_start_input_reader_thread()` (in `cli.py`) that feeds lines into a `queue.SimpleQueue`:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aw Created by agentic workflow

Projects

None yet

2 participants