def execute_command(self, command, cwd=None, stdout_captured=None): """Execute a command at cwd, saving its normal output at stdout_captured. Errors, defined as nonzero return code or a failure to start execution, will raise a CompilerError exception with a description of the cause. They do not write output. This is file-system safe (any valid file names are allowed, even with spaces or crazy characters) and OS agnostic (existing and future OSes that Python supports should already work). The only thing weird here is that any incoming command arg item may itself be a tuple. This allows compiler implementations to look clean while supporting historical string config settings and maintaining backwards compatibility. Thus, we flatten one layer deep. ((env, foocomp), infile, (-arg,)) -> (env, foocomp, infile, -arg) """ argument_list = [] for flattening_arg in command: if isinstance(flattening_arg, string_types): argument_list.append(flattening_arg) else: argument_list.extend(flattening_arg) # The first element in argument_list is the program that will be executed; if it is '', then # a PermissionError will be raised. Thus empty arguments are filtered out from argument_list argument_list = filter(None, argument_list) stdout = None try: # We always catch stdout in a file, but we may not have a use for it. temp_file_container = cwd or os.path.dirname(stdout_captured or "") or os.getcwd() with NamedTemporaryFile(delete=False, dir=temp_file_container) as stdout: compiling = subprocess.Popen(argument_list, cwd=cwd, stdout=stdout, stderr=subprocess.PIPE) _, stderr = compiling.communicate() set_std_streams_blocking() if compiling.returncode != 0: stdout_captured = None # Don't save erroneous result. raise CompilerError( "{0!r} exit code {1}\n{2}".format(argument_list, compiling.returncode, stderr), command=argument_list, error_output=stderr) # User wants to see everything that happened. if self.verbose: with open(stdout.name) as out: print(out.read()) print(stderr) except OSError as e: stdout_captured = None # Don't save erroneous result. raise CompilerError(e, command=argument_list, error_output=text_type(e)) finally: # Decide what to do with captured stdout. if stdout: if stdout_captured: shutil.move(stdout.name, os.path.join(cwd or os.curdir, stdout_captured)) else: os.remove(stdout.name)
def execute_command(self, command, content): argument_list = [] for flattening_arg in command: if isinstance(flattening_arg, string_types): argument_list.append(flattening_arg) else: argument_list.extend(flattening_arg) pipe = subprocess.Popen(argument_list, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) if content: content = smart_bytes(content) stdout, stderr = pipe.communicate(content) set_std_streams_blocking() if stderr.strip() and pipe.returncode != 0: raise CompressorError(stderr) elif self.verbose: print(stderr) return force_text(stdout)
def execute_command(self, command, content): argument_list = [] for flattening_arg in command: if isinstance(flattening_arg, (str,)): argument_list.append(flattening_arg) else: argument_list.extend(flattening_arg) pipe = subprocess.Popen(argument_list, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) if content: content = smart_bytes(content) stdout, stderr = pipe.communicate(content) set_std_streams_blocking() if stderr.strip() and pipe.returncode != 0: raise CompressorError(stderr) elif self.verbose: print(stderr) return force_text(stdout)
def execute_command(self, command, cwd=None, stdout_captured=None): """Execute a command at cwd, saving its normal output at stdout_captured. Errors, defined as nonzero return code or a failure to start execution, will raise a CompilerError exception with a description of the cause. They do not write output. This is file-system safe (any valid file names are allowed, even with spaces or crazy characters) and OS agnostic (existing and future OSes that Python supports should already work). The only thing weird here is that any incoming command arg item may itself be a tuple. This allows compiler implementations to look clean while supporting historical string config settings and maintaining backwards compatibility. Thus, we flatten one layer deep. ((env, foocomp), infile, (-arg,)) -> (env, foocomp, infile, -arg) """ argument_list = [] for flattening_arg in command: if isinstance(flattening_arg, string_types): argument_list.append(flattening_arg) else: argument_list.extend(flattening_arg) # The first element in argument_list is the program that will be executed; if it is '', then # a PermissionError will be raised. Thus empty arguments are filtered out from argument_list argument_list = list(filter(None, argument_list)) stdout = None try: # We always catch stdout in a file, but we may not have a use for it. temp_file_container = cwd or os.path.dirname(stdout_captured or "") or os.getcwd() with NamedTemporaryFile(delete=False, dir=temp_file_container) as stdout: compiling = subprocess.Popen(argument_list, cwd=cwd, stdout=stdout, stderr=subprocess.PIPE) _, stderr = compiling.communicate() set_std_streams_blocking() if compiling.returncode != 0: stdout_captured = None # Don't save erroneous result. raise CompilerError("{0!r} exit code {1}\n{2}".format( argument_list, compiling.returncode, stderr), command=argument_list, error_output=stderr) # User wants to see everything that happened. if self.verbose: with open(stdout.name) as out: print(out.read()) print(stderr) except OSError as e: stdout_captured = None # Don't save erroneous result. raise CompilerError(e, command=argument_list, error_output=text_type(e)) finally: # Decide what to do with captured stdout. if stdout: if stdout_captured: shutil.move( stdout.name, os.path.join(cwd or os.curdir, stdout_captured)) else: os.remove(stdout.name)