Commit fd0be33a by Frankie Dintino

Set stdout and stderr to blocking after running an external command

Some nodejs executables set stdout and/or stderr to non-blocking, which
can trigger an IOError when the collectstatic command prints more than
1024 bytes at a time to stdout or stderr
parent 74884f33
...@@ -12,7 +12,7 @@ from django.utils.six import string_types ...@@ -12,7 +12,7 @@ from django.utils.six import string_types
from pipeline.conf import settings from pipeline.conf import settings
from pipeline.exceptions import CompilerError from pipeline.exceptions import CompilerError
from pipeline.utils import to_class from pipeline.utils import to_class, set_std_streams_blocking
class Compiler(object): class Compiler(object):
...@@ -120,6 +120,7 @@ class SubProcessCompiler(CompilerBase): ...@@ -120,6 +120,7 @@ class SubProcessCompiler(CompilerBase):
stdout=stdout, stdout=stdout,
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
_, stderr = compiling.communicate() _, stderr = compiling.communicate()
set_std_streams_blocking()
if compiling.returncode != 0: if compiling.returncode != 0:
stdout_captured = None # Don't save erroneous result. stdout_captured = None # Don't save erroneous result.
......
...@@ -14,7 +14,7 @@ from django.utils.six import string_types ...@@ -14,7 +14,7 @@ from django.utils.six import string_types
from pipeline.conf import settings from pipeline.conf import settings
from pipeline.exceptions import CompressorError from pipeline.exceptions import CompressorError
from pipeline.utils import to_class, relpath from pipeline.utils import to_class, relpath, set_std_streams_blocking
URL_DETECTOR = r"""url\((['"]){0,1}\s*(.*?)["']{0,1}\)""" URL_DETECTOR = r"""url\((['"]){0,1}\s*(.*?)["']{0,1}\)"""
URL_REPLACER = r"""url\(__EMBED__(.+?)(\?\d+)?\)""" URL_REPLACER = r"""url\(__EMBED__(.+?)(\?\d+)?\)"""
...@@ -248,6 +248,7 @@ class SubProcessCompressor(CompressorBase): ...@@ -248,6 +248,7 @@ class SubProcessCompressor(CompressorBase):
if content: if content:
content = smart_bytes(content) content = smart_bytes(content)
stdout, stderr = pipe.communicate(content) stdout, stderr = pipe.communicate(content)
set_std_streams_blocking()
if stderr.strip() and pipe.returncode != 0: if stderr.strip() and pipe.returncode != 0:
raise CompressorError(stderr) raise CompressorError(stderr)
elif self.verbose: elif self.verbose:
......
from __future__ import unicode_literals from __future__ import unicode_literals
try:
import fcntl
except ImportError:
# windows
fcntl = None
import importlib import importlib
import mimetypes import mimetypes
import posixpath import posixpath
import os
import sys
try: try:
from urllib.parse import quote from urllib.parse import quote
...@@ -54,3 +62,19 @@ def relpath(path, start=posixpath.curdir): ...@@ -54,3 +62,19 @@ def relpath(path, start=posixpath.curdir):
if not rel_list: if not rel_list:
return posixpath.curdir return posixpath.curdir
return posixpath.join(*rel_list) return posixpath.join(*rel_list)
def set_std_streams_blocking():
"""
Set stdout and stderr to be blocking.
This is called after Popen.communicate() to revert stdout and stderr back
to be blocking (the default) in the event that the process to which they
were passed manipulated one or both file descriptors to be non-blocking.
"""
if not fcntl:
return
for f in (sys.__stdout__, sys.__stderr__):
fileno = f.fileno()
flags = fcntl.fcntl(fileno, fcntl.F_GETFL)
fcntl.fcntl(fileno, fcntl.F_SETFL, flags & ~os.O_NONBLOCK)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment