pip
pip copied to clipboard
pip list |head raises BrokenPipeError
Description
https://github.com/pypa/pip/issues/4170 is closed but the bug is still there not sure if it's a regression or the fix may have missed something in the first place (@cjerdonek?). I could not bisect it, because I also get the same issue on pip 19.0.0 which is when the fix went in according to the changelog. reproducible on both macOS and Linux.
Expected behavior
pip handles BrokenPipeError
and exits gracefully
pip version
22.3.1
Python version
3.11.0
OS
macOS and/or Linux
How to Reproduce
$ pip --version
pip 22.3.1 from .../.venv/lib/python3.11/site-packages/pip (python 3.11)
$ head --version
head (GNU coreutils) 8.30
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by David MacKenzie and Jim Meyering.
$ pip list | head -1
Package Version Editable project location
ERROR: Pipe to stdout was broken
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pipe
Interestingly, it doesn't happen for pip freeze
$ pip freeze | head -1
anytree==2.8.0
❯ pip list | head -1
Package Version Editable project location
ERROR: Pipe to stdout was broken
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pipe
❯ echo $?
0
What behaviour are you expecting to be different?
The final three lines are basically providing a bunch of context, on the fact that the stdout pipe was broken (they're all at different levels within pip's logging and CLI infra).
I'm not talking about the return code, I just think this should not cause any print output on stderr. It could be debug or info log events, if anything, so it's not shown by default (i.e. without increased verbosity -v
flag).
head
closing stdin is normal, isn't it? So that shouldn't be a ERROR event printed from pip, it's not really an error condition?
basically I'm expecting it to behave like pip 21.x does here on python 3.8:
$ pip list | head -1
Package Version
WARNING: You are using pip version 21.3.1; however, version 22.3.1 is available.
You should consider upgrading via the '/Users/wim/opt/miniconda3/envs/pb/bin/python3 -m pip install --upgrade pip' command.
$ /tmp pip list --disable-pip-version-check | head -1
Package Version
$ /tmp echo $?
0
$ pip install -U pip
Requirement already satisfied: pip in /Users/wim/opt/miniconda3/envs/pb/lib/python3.8/site-packages (21.3.1)
Collecting pip
Using cached pip-22.3.1-py3-none-any.whl (2.1 MB)
Installing collected packages: pip
Attempting uninstall: pip
Found existing installation: pip 21.3.1
Uninstalling pip-21.3.1:
Successfully uninstalled pip-21.3.1
Successfully installed pip-22.3.1
$ /tmp pip list | head -1
Package Version
ERROR: Pipe to stdout was broken
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pipe
head
closing stdin is normal, isn't it?
It closed stdout in this case, which caused an error within pip's output stack. That error is suppressed (in terms of the exit code), but presented across three lines.
Why is that an error condition for pip list, but not for pip freeze (or earlier releases of pip)?
OS Name: macOS
OS Version: 13.0.1
Chip: Apple M1 Max
Memory: 64 GB
pip version: 22.3.1

It's intermittent in iterm2:
❯ pip list | head -n2
Package Version
---------- -------
❯ pip list | head -n2
Package Version
---------- -------
❯ pip list | head -n2
Package Version
---------- -------
❯ pip list | head -n2
Package Version
---------- -------
ERROR: Pipe to stdout was broken
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pipe
❯ pip --version
pip 23.1.2 from /Users/tekumara/.virtualenvs/tmp-c317f937f66cda0/lib/python3.9/site-packages/pip (python 3.9)
But I can reproduce it every time in a vscode terminal.
BTW tail
can be used to consume all output and cleanly exit, effectively handling any broken pipes,
eg: this won't error
❯ pip list | tail -n +1 | head -n1
Package Version
I think the standard thing to do with SIGPIPE
and EPIPE
is to silently exit with a non zero status code. This is the default behavior under POSIX, but Python ignores the SIGPIPE
and turns the EPIPE
into the BrokenPipeError
error.
I think just removing these lines would restore pip back to the default behavior on POSIX, and arguably what people expect to happen:
https://github.com/pypa/pip/blob/f25f8fffbbd16fdb13a4f8977946afe9a3248453/src/pip/_internal/cli/base_command.py#L198-L202
Also note that $?
on pip list | head -1
will be the return code for head
, not for pip. To get the return code for pip you need ${PIPESTATUS[0]}
Oh, and the error status should be 141 IIRC, convention is to return with 128 + the signal number.
+1 for the solution proposed by @dstufft. It is unusual and annoying to get extra BrokenPipeError output when it isn't expected. Adding a flag to suppress this output would be helpful at the very least.