PyCQA/flake8

Piping flake8 to head causes ungraceful "Broken pipe" handling in formatting/base.py

Torxed opened this issue · 3 comments

how did you install flake8?

$ pacman -Sy flake8

unmodified output of flake8 --bug-report

{
  "platform": {
    "python_implementation": "CPython",
    "python_version": "3.12.3",
    "system": "Linux"
  },
  "plugins": [
    {
      "plugin": "mccabe",
      "version": "0.7.0"
    },
    {
      "plugin": "pycodestyle",
      "version": "2.11.1"
    },
    {
      "plugin": "pyflakes",
      "version": "3.2.0"
    }
  ],
  "version": "7.0.0"
}

describe the problem

what I expected to happen

Flake8 handling sys.stdout closure gracefully.

sample code

Any code will work, and will generate:

Traceback (most recent call last):
  File "/usr/bin/flake8", line 33, in <module>
    sys.exit(load_entry_point('flake8==7.0.0', 'console_scripts', 'flake8')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/flake8/main/cli.py", line 23, in main
    app.run(argv)
  File "/usr/lib/python3.12/site-packages/flake8/main/application.py", line 198, in run
    self._run(argv)
  File "/usr/lib/python3.12/site-packages/flake8/main/application.py", line 188, in _run
    self.report()
  File "/usr/lib/python3.12/site-packages/flake8/main/application.py", line 180, in report
    self.report_errors()
  File "/usr/lib/python3.12/site-packages/flake8/main/application.py", line 141, in report_errors
    results = self.file_checker_manager.report()
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/flake8/checker.py", line 190, in report
    results_reported += self._handle_results(filename, results)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/flake8/checker.py", line 166, in _handle_results
    reported_results_count += style_guide.handle_error(
                              ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/flake8/style_guide.py", line 294, in handle_error
    return guide.handle_error(
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/flake8/style_guide.py", line 428, in handle_error
    self.formatter.handle(error)
  File "/usr/lib/python3.12/site-packages/flake8/formatting/base.py", line 99, in handle
    self.write(line, source)
  File "/usr/lib/python3.12/site-packages/flake8/formatting/base.py", line 196, in write
    self._write(source)
  File "/usr/lib/python3.12/site-packages/flake8/formatting/base.py", line 178, in _write
    sys.stdout.buffer.write(output.encode() + self.newline.encode())
BrokenPipeError: [Errno 32] Broken pipe
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pipe

commands ran

$ flake8 . | head

this is a common problem with python itself due to some "interesting" choices in the Io implementation

try for instance with a print loop

Can you elaborate on "try a print loop"?
I feel this is a flake8 cli tooling issue, where it's missing something along the lines of:

try:
    sys.stdout.buffer.write(...)
except BrokenPipeError:
    exit()

@Torxed printing to a tty (or pty) is not the only use-case of Flake8. It's possible for someone to print to both stdout and to a file simultaneously with CLI flags. If we simply exit when something goes wrong (and do so silently) then we truncate what we write to the file without notice. If instead we just ignore every BrokenPipeError so writing to the file succeeds, what do we do then? Exit with non-zero somehow?

You're proposing a simple solution to something which fits your simplistic mental model which happens to be wrong.

The better solution is to fail loudly so the user knows that something went wrong unexpectedly. The point Anthony is making above is that Python has chosen to implement things this way. Other languages don't break in this particular manner, so fundamentally this is a flaw in Python.