Skip to content

Commit

Permalink
Merge pull request #1924 from SublimeLinter/broken-pipe
Browse files Browse the repository at this point in the history
  • Loading branch information
kaste committed Feb 10, 2024
2 parents 1057c1b + 0ed9cac commit 61849c6
Showing 1 changed file with 43 additions and 0 deletions.
43 changes: 43 additions & 0 deletions lint/linter.py
Expand Up @@ -10,6 +10,7 @@
import re
import shlex
import subprocess
import sys
import tempfile

import sublime
Expand Down Expand Up @@ -1725,6 +1726,14 @@ def _communicate(self, cmd, code=None):
'<pid {}>'.format(proc.pid)
)
raise TransientError('Friendly terminated')

if sys.platform == "win32":
try:
out = recover_broken_pipe(proc)
except Exception as err:
self.logger.warning('Exception: {}'.format(str(err)))
self.notify_failure()
raise PermanentError("non-friendly broken pipe")
else:
self.logger.warning('Exception: {}'.format(str(err)))
self.notify_failure()
Expand All @@ -1749,6 +1758,40 @@ def _communicate(self, cmd, code=None):
return util.popen_output(proc, *out)


# Old python versions do not protect (typically: ignore) against
# `BrokenPipeError`s enough. I.e. within `Popen._communicate` there is (still)
# an unprotected call to `self.stdin.close()`. This has been fixed rather late
# in Python v3.5, June 2016.
# Ref: https://github.com/python/cpython/commit/1ef8c7e886ea5260e5a6967ec2b8a4c32640f1a8
# The following is verbatim the code that comes after closing `stdin`, unchanged
# since v3.3.7 'til Feb 2024.
def recover_broken_pipe(self): # self refers `subprocess.Popen`
# Wait for the reader threads.
if self.stdout is not None:
self.stdout_thread.join(None)
if self.stderr is not None:
self.stderr_thread.join(None)

# Collect the output from and close both pipes, now that we know
# both have been read successfully.
stdout = None
stderr = None
if self.stdout:
stdout = self._stdout_buff
self.stdout.close()
if self.stderr:
stderr = self._stderr_buff
self.stderr.close()

# All data exchanged. Translate lists into strings.
if stdout is not None:
stdout = stdout[0]
if stderr is not None:
stderr = stderr[0]

return (stdout, stderr)


@contextmanager
def make_temp_file(suffix, code):
# type: (str, str) -> Iterator[IO]
Expand Down

0 comments on commit 61849c6

Please sign in to comment.