Skip to content
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

Teach pip to play along with head unix-command #5721

Closed
wants to merge 5 commits into from

Conversation

ankostis
Copy link

@ankostis ankostis commented Aug 21, 2018

Fix #4170: Override ColorizedStreamHandler.flush() to ignore BrokenPipeError exceptions.

Note, it would be hard to make a test-case for it, no?

@ankostis ankostis changed the title Teach log-handler to stay calm on broken-pipes eg. pip | head WIP: Teach log-handler to stay calm on broken-pipes eg. pip | head Aug 21, 2018
"""Ignore `BrokenPipeErrors` on `flush()`."""
try:
self.flush()
except BrokenPipeError:
Copy link
Member

Choose a reason for hiding this comment

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

BrokenPipeError wasn't defined in Python 2, so you'll need something different for that case

@pfmoore
Copy link
Member

pfmoore commented Aug 21, 2018

Note, it would be hard to make a test-case for it, no?

Well, you said that pip list | head fails with an error and it shouldn't. That sounds easy enough to write a test for 😄

@pradyunsg pradyunsg added state: needs discussion This needs some more discussion T: bugfix labels Aug 21, 2018
@pradyunsg pradyunsg changed the title WIP: Teach log-handler to stay calm on broken-pipes eg. pip | head [WIP] Teach log-handler to stay calm on broken-pipes eg. pip | head Aug 21, 2018
def flush(self):
"""Ignore `BrokenPipeErrors` on `flush()`."""
try:
self.flush()
Copy link
Member

Choose a reason for hiding this comment

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

This should be super(..., self).flush().

news/4170.bugfix Outdated
@@ -0,0 +1,2 @@
Teach pip's logging-handler to stay calm on `BrokenPipeError`, probably *stderr* redirected to :command:`head`.
Copy link
Member

@pradyunsg pradyunsg Aug 21, 2018

Choose a reason for hiding this comment

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

This should probably be something like:

Avoid printing a Traceback when a broken pipe is encountered in stderr (like when piping through `head` on Unix)

@ankostis
Copy link
Author

Any idea where to attache the TC?

@pradyunsg
Copy link
Member

@ankostis What's TC?

@ankostis
Copy link
Author

What's TC?

The one suggested by @pfmoore.
Which file should i put it?

  • tests/scripts/test_all_pip.py
  • tests/unit/test_base_command:Test_base_command_logging

@pfmoore
Copy link
Member

pfmoore commented Aug 21, 2018

What's TC?

I'm guessing "test case". I don't really have any good feel for where would be the best place in the test suite for something like this, so I can't really suggest a location, I'm afraid.

@ankostis
Copy link
Author

The most relevant TC is tests/functional/test_no_color.py bc it runs pip as external command, and checks the color-logging-handler. But the name is a bit irrelevant.

I will put it here, and someone may rename this test-module, if you don't have any objections.

@pradyunsg
Copy link
Member

You can add that test there and rename that file to test_logging.py.

@ankostis
Copy link
Author

Without the TC yet, travis is ok.

Note that pip list, suggested above, writes to STDOUT, whuch is not handled by logging, and not addressed by this PR - i would prefer not to extend this PR to that.

@pradyunsg
Copy link
Member

Extend it to renaming the file? No worries. I'll be okay with doing it myself in a follow up.

@ankostis
Copy link
Author

No, ididn' mean that., i can rename it the test-case module.

I guess pip has 2 "pipelines" for output, 1) logging, 2) pure writes to stdout/stderr, correct.
This PR deals with (2) logging only.

@@ -136,12 +137,25 @@ def get_win32_calls(self):
}
return dict()

def _flush(self):
Copy link
Member

Choose a reason for hiding this comment

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

You've modified a vendored library - colorama. What's the reason you had to do this?

pip actively avoids patching it's vendored libraries in favour of making changes upstream and absorbing those changes when a new release is made.

Copy link
Author

@ankostis ankostis Aug 21, 2018

Choose a reason for hiding this comment

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

As explained it also in conversation, patching logger is not enough.

In windows, as soon as you patch it and try to redirect to head, that this the next point where it fails on broken-pipe.
And not only it simply fails, but if you ignore also at that point pipe-errors, you then get a mysterious EINVAL OSError:

   File "pip\_vendor\colorama\ansitowin32.py", line 143, in _flush
   self.wrapped.flush()
   OSError: [Errno 22] Invalid argument

If you ignore also that, then it works.

Now i'm also surprised that Colorama has not fixed it, and i search a bit and did not fimnd anything related reported.
Certainly if it is fixable, it should be fixed there.

@ankostis ankostis force-pushed the fix_logging_broken_pipes branch 3 times, most recently from c82fdcd to 28beeeb Compare August 22, 2018 15:28
bc on Windows it breaks with OSError(errno.EINVAL) after pip has broken.
+ Fix a bit the TC according to PY2 behavior.
@ankostis
Copy link
Author

ankostis commented Aug 22, 2018

Ok another day, another look....i abandoned pip list | head and mimicked its behavior with Popen(), then closing its stdout prematurely and counting the lines printed in stderr. It is there that the standard logging subsystem dumps stack-traces of exceptions collected while emitting log-records.

2 new TCs have been introduced in the renamed test_loggin.py file, committed before the fix-commits of this PR, to compare behavior before and after:

  1. test_broken_pipe_output() TC which does a "sanity" output check whether pip freeze breaks politely on broken-pipes. The freeze command is special, in that it writes stdout in a loop - all the rest commands are using logging system to print their results (relevant to next TC). Therefore:

    • even before the fix (7f4ec24, add TCs), a broken-pipe-error breaks immediately the command, and the TC passes (travis#419216427).
    • This behavior remains unaffected after the fixes.
  2. The test_broken_pipe_logger() TC checks politeness of download cmd (many logs) against broken-pipes.

    • Before the fix, it is impolite, and emits ~5-6 log lines per each of-statement (~5k lines).
    • After the 1st fix-commit(28beeeb , patch logHandler), it works on Linux, but keeps failing on Windows, due to colorama (appveyor#1.0.3221), as discussed earlier.
    • After the 2nd (b84dfe7, patch colorama), it stops emitting more logs, yet the command continues to run until the end ( travis#9449).

PR graduated from [WIP] (but still not decided whether to push upstream fix to colorama).

@ankostis
Copy link
Author

Note: At the moment have pushed one commit back in time from b84dfe7, and wait for Appveyor to validate it.

"Expected no color in output"


def _run_and_brake_stdout(cmd, read_nchars=1, check=False, **popen_kw):
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Spelling is "break" not "brake".

Copy link
Author

Choose a reason for hiding this comment

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

done


def _run_and_brake_stdout(cmd, read_nchars=1, check=False, **popen_kw):
"""
Launch Popen, brake stdout, ignore returncode and return stderr.
Copy link
Member

Choose a reason for hiding this comment

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

Also here

Copy link
Author

Choose a reason for hiding this comment

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

done

if check:
assert not proc.returncode, (proc.returncode, cmd, err)

return err
Copy link
Member

Choose a reason for hiding this comment

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

This actually seems way too complicated. I'd be concerned that there are bugs in the test code here. Not that I can see any, just that it's not obvious what it's testing.

To reiterate what I said originally, what's wrong with something as simple as

text = subprocess.check_output('pip --help | head`, stderr=subprocess.STDOUT, shell=True)
assert 'Broken pipe' in text

You may have to only run the test on Unix, as Windows doesn't have a head command by default, but I wouldn't be too worried about that. To be fair though, I'm not actually convinced that the broken pipe error is bad enough to be worth worrying about in the first place...

Copy link
Author

@ankostis ankostis Aug 22, 2018

Choose a reason for hiding this comment

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

Windows doesn't have a head command ... but I wouldn't be too worried about that

No no, there's MSYS2 (posix) on appveyor (among others), head wouldn't be a problem.
Testing on Windows is important. You see, i discovered the colorama problem, captured by the TCs, (appveyor#1.0.3221), and fixed by the 2nd fix-commit.

what's wrong with something as simple as subprocess.check_output('pip --help | head' ...

I've pushed the simplified variant you suggested, and the strange errors started.
Additionally, in Windows i'm infested by #3383 and cannot test cleanly locally, and going back and forth to virtualbox is tiring...
I remember these 2 days fighting them, my apologies, i don't remember all the nuances that made me turn around.
Any ideas?

BTW: pip --help is not printing through logger, which this PR is about, but through argparse (no?) - we need a command with many separate log-statements, like download, or install. Is there any other?

I'm not actually convinced that the broken pipe error is bad enough

I scratched my itch when i was fighting package/version resolution problems, and used head to view just the few first lines of the logs, but still, a torrent of 5000+ lines scrolled down my console too quickly for Ctrl+C to work - i don't know how to say it, but a tool not going along with head feels rude to me (hope it's not too impolite to say it).

Copy link
Member

Choose a reason for hiding this comment

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

a tool not going along with head feels rude to me (hope it's not too impolite to say it).

Not at all, and if it caused you a problem and you were considerate enough to spend time fixing it for us, I'm very grateful. All I was trying to say was that I'm not a good judge of how much effort it's worth spending on this, as it doesn't bother me that much.

I understand about the annoyances testing. I don't know of a good answer - I do basically what you do, I push a WIP change and let the CI run the tests. What you can do, once you have a specific test that you want to work on, is run that test alone using tox -e py36 -- tests\functional\test_logging.py -k test_broken_pipe_logger specifying the file and test name. That's usually fast enough to be workable.

I'll try to find some time tomorrow and look through the bugs you're hitting, and see if I can shed any light on them.

@ankostis ankostis force-pushed the fix_logging_broken_pipes branch 3 times, most recently from edc5a84 to b096426 Compare August 23, 2018 11:41
@ankostis
Copy link
Author

Created a new PR #5729 with the pip | head test-case, to keep this pure-python variant that is working ok, clean.

@ankostis ankostis changed the title [WIP] Teach log-handler to stay calm on broken-pipes eg. pip | head Teach log-handler to stay calm on broken-pipes eg. pip | head Aug 23, 2018
@ankostis ankostis changed the title Teach log-handler to stay calm on broken-pipes eg. pip | head Teach pip to play along with head unix-command Aug 23, 2018
@cjerdonek
Copy link
Member

Closing this for the same reason mentioned in PR #5729 (superseded by PR #5907, which was recently merged): #5729 (comment) Thanks for your contributions, @ankostis.

@cjerdonek cjerdonek closed this Jan 21, 2019
@lock
Copy link

lock bot commented May 30, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot added the auto-locked Outdated issues that have been locked by automation label May 30, 2019
@lock lock bot locked as resolved and limited conversation to collaborators May 30, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
auto-locked Outdated issues that have been locked by automation state: needs discussion This needs some more discussion
Projects
None yet
Development

Successfully merging this pull request may close these issues.

pip list |head raises BrokenPipeError
4 participants