-
Notifications
You must be signed in to change notification settings - Fork 19
Fix subprocess.communicate("")
raising IOError: closed stream
#78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This test demonstrates a bug in subprocess.communicate where passing an empty string ("") causes a "closed stream" error. The issue only happens with an empty string input, not with nil or non-empty strings. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Previously, when passing an empty string to communicate(), stdin would be closed immediately but then later used in IO.select calls, causing a "closed stream" error. This fix: 1. Only closes stdin immediately for nil input 2. For empty string input, closes stdin after determining wait_w and removes it from the wait_w array to prevent IO.select from using it 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Improvements made: 1. Cleaner logic in communicate() method - explicitly handle wait_w array setup 2. Better edge case handling - check for already closed stdin 3. Improved tests that directly reproduce the original bug condition 4. Added comprehensive test coverage for nil, empty string, and edge cases The fix now properly handles: - Empty string input: closes stdin immediately, doesn't add to IO.select - Nil input: closes stdin immediately (existing behavior) - Non-empty input: adds stdin to wait_w for writing (existing behavior) - Already closed stdin: gracefully handled without errors 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Improves code readability by: - Using stdin_closed variable to track state instead of repeating checks - Maintaining clear separation between nil and empty string handling - Eliminating repetitive @stdin.closed? checks - Preserving original logic flow and behavior 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Improvements: - Move empty string handling to top with nil handling - Use single stdin_closed variable as source of truth - Handle pre-closed stdin edge case properly - Simplify wait_w logic to just check \!stdin_closed This creates cleaner, more maintainable code with the same behavior. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
86ecd92
to
642b66f
Compare
There was a problem hiding this 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 fixes an IOError: closed stream
bug that occurred when calling subprocess.communicate("")
with an empty string input. The issue arose because the code closed stdin when input was empty but then tried to use the closed file descriptor in IO.select
.
- Fixed stdin handling logic to exclude closed stdin from
IO.select
operations - Added comprehensive test coverage for empty string, nil, and non-empty string inputs
- Ensured backward compatibility by only fixing the broken empty string case
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
File | Description |
---|---|
lib/subprocess.rb | Updated stdin handling to check if stdin is closed before including it in wait operations |
test/test_empty_stdin.rb | Added comprehensive test suite covering empty string, nil input, non-empty string, and edge cases |
Comments suppressed due to low confidence (1)
test/test_empty_stdin.rb:45
- This test creates a subprocess but doesn't handle cleanup if an assertion fails before
p.wait
is called. Consider wrapping in an ensure block or using the block form ofcheck_call
for automatic cleanup.
p = Subprocess.popen(['cat'], stdin: Subprocess::PIPE, stdout: Subprocess::PIPE)
subprocess.communicate("")
would raiseIOError: closed stream
because the code closed stdin immediately wheninput.empty?
, but then later tried to use the closed stdin file descriptor inIO.select
This only occurs with empty strings (
""
), not withnil
or non-empty strings.We've fixed that by excluding
@stdin
fromwait_w
when@stdin.closed?
Backward compatibility
This fix maintains full backward compatibility:
nil
input behaviour unchanged