def function(fn, *args, **kwargs): #pylint: disable=unused-variable import inspect, sys from mrtrix3 import app fnstring = fn.__module__ + '.' + fn.__name__ + \ '(' + ', '.join(['\'' + str(a) + '\'' if isinstance(a, str) else str(a) for a in args]) + \ (', ' if (args and kwargs) else '') + \ ', '.join([key+'='+str(value) for key, value in kwargs.items()]) + ')' if _lastFile: if _triggerContinue(args) or _triggerContinue(kwargs.values()): app.debug('Detected last file in function \'' + fnstring + '\'; this is the last run.command() / run.function() call that will be skipped') if app.verbosity: sys.stderr.write(app.colourExec + 'Skipping function:' + app.colourClear + ' ' + fnstring + '\n') sys.stderr.flush() return None if app.verbosity: sys.stderr.write(app.colourExec + 'Function:' + app.colourClear + ' ' + fnstring + '\n') sys.stderr.flush() # Now we need to actually execute the requested function try: if kwargs: result = fn(*args, **kwargs) else: result = fn(*args) except Exception as e: # pylint: disable=broad-except app.cleanup = False caller = inspect.getframeinfo(inspect.stack()[1][0]) error_text = str(type(e).__name__) + ': ' + str(e) script_name = os.path.basename(sys.argv[0]) app.console('') try: filename = caller.filename lineno = caller.lineno except AttributeError: filename = caller[1] lineno = caller[2] sys.stderr.write(script_name + ': ' + app.colourError + '[ERROR] Function failed: ' + fnstring + app.colourClear + app.colourDebug + ' (' + os.path.basename(filename) + ':' + str(lineno) + ')' + app.colourClear + '\n') sys.stderr.write(script_name + ': ' + app.colourConsole + 'Information from failed function:' + app.colourClear + '\n') for line in error_text.splitlines(): sys.stderr.write(' ' * (len(script_name)+2) + line + '\n') app.console('') sys.stderr.flush() if app.tempDir: with open(os.path.join(app.tempDir, 'error.txt'), 'w') as outfile: outfile.write(fnstring + '\n\n' + error_text + '\n') app.complete() sys.exit(1) # Only now do we append to the script log, since the function has completed successfully if app.tempDir: with open(os.path.join(app.tempDir, 'log.txt'), 'a') as outfile: outfile.write(fnstring + '\n') return result
os.path.join(subject_dir, 'subject2template_warp.mif') + ' -fc ' + os.path.join(template_dir, 'fixel_mask') + ' ' + template_fc + ' ' + subject_label + '.mif' + ' -force') # Compute log FC if not os.path.exists(os.path.join(template_dir, 'log_fc')): os.mkdir(os.path.join(template_dir, 'log_fc')) run.command('mrcalc ' + os.path.join(template_fc, subject_label + '.mif') + ' -log ' + template_log_fc + ' -force') if not os.path.exists(os.path.join(template_dir, 'fdc')): os.mkdir(os.path.join(template_dir, 'fdc')) run.command('mrcalc ' + os.path.join(template_fd, subject_label + '.mif') + ' ' + os.path.join(template_fc, subject_label + '.mif') + ' -mult ' + template_fdc + ' -force') # Copy index and directions file into log FC and FDC fixel directories run.command('cp ' + os.path.join(template_fc, 'index.mif') + ' ' + os.path.join(template_fc, 'directions.mif') + ' ' + os.path.join(template_dir, 'log_fc')) run.command('cp ' + os.path.join(template_fc, 'index.mif') + ' ' + os.path.join(template_fc, 'directions.mif') + ' ' + os.path.join(template_dir, 'fdc')) # Perform fixel-based statistical inference in FD, FC and FDC elif app.args.analysis_level == "group4": print('asdf') app.complete()
DKI=0 if app.args.DKIparams: DKI=1 WMTI=0 if app.args.WMTIparams: WMTI=1 constraints=0 if app.args.fit_constraints: constraints=app.args.fit_constraints AKC=0 if app.args.akc: AKC=1 KCUM=0 if app.args.kcumulants: KCUM=1 eng.tensorfitting(path.toTemp('',True),path.fromUser(app.args.output, True),outliers,KCUM,DTI,DKI,WMTI,constraints,AKC,DKI_root,nargout=0) eng.quit() app.gotoTempDir() else: if app.args.datatype: run.command('mrconvert -datatype ' + app.args.datatype + ' ' + path.toTemp('dwi_designer.nii',True) + ' ' + path.fromUser(app.args.output + '.nii',True)) else: shutil.copyfile(path.toTemp('dwi_designer.nii',True),path.fromUser(app.args.output + '.nii',True)) shutil.copyfile(path.toTemp('dwi_designer.bvec',True),path.fromUser(app.args.output + '.bvec',True)) shutil.copyfile(path.toTemp('dwi_designer.bval',True),path.fromUser(app.args.output + '.bval',True)) app.complete()
def command(cmd, exitOnError=True): #pylint: disable=unused-variable import inspect, itertools, shlex, signal, string, subprocess, sys, tempfile from distutils.spawn import find_executable from mrtrix3 import app # This is the only global variable that is _modified_ within this function global _processes # Vectorise the command string, preserving anything encased within quotation marks if os.sep == '/': # Cheap POSIX compliance check cmdsplit = shlex.split(cmd) else: # Native Windows Python cmdsplit = [ entry.strip('\"') for entry in shlex.split(cmd, posix=False) ] if _lastFile: if _triggerContinue(cmdsplit): app.debug( 'Detected last file in command \'' + cmd + '\'; this is the last run.command() / run.function() call that will be skipped' ) if app.verbosity: sys.stderr.write(app.colourExec + 'Skipping command:' + app.colourClear + ' ' + cmd + '\n') sys.stderr.flush() return ('', '') # This splits the command string based on the piping character '|', such that each # individual executable (along with its arguments) appears as its own list cmdstack = [ list(g) for k, g in itertools.groupby(cmdsplit, lambda s: s != '|') if k ] for line in cmdstack: is_mrtrix_exe = line[0] in _mrtrix_exe_list if is_mrtrix_exe: line[0] = versionMatch(line[0]) if app.numThreads is not None: line.extend(['-nthreads', str(app.numThreads)]) # Get MRtrix3 binaries to output additional INFO-level information if running in debug mode if app.verbosity == 3: line.append('-info') elif not app.verbosity: line.append('-quiet') else: line[0] = exeName(line[0]) shebang = _shebang(line[0]) if shebang: if not is_mrtrix_exe: # If a shebang is found, and this call is therefore invoking an # interpreter, can't rely on the interpreter finding the script # from PATH; need to find the full path ourselves. line[0] = find_executable(line[0]) for item in reversed(shebang): line.insert(0, item) app.debug('To execute: ' + str(cmdstack)) if app.verbosity: sys.stderr.write(app.colourExec + 'Command:' + app.colourClear + ' ' + cmd + '\n') sys.stderr.flush() # Disable interrupt signal handler while threads are running try: signal.signal(signal.SIGINT, signal.default_int_handler) except: pass # Construct temporary text files for holding stdout / stderr contents when appropriate # (One entry per process; each is a tuple containing two entries, each of which is either a # file-like object, or None) tempfiles = [] # Execute all processes assert not _processes for index, to_execute in enumerate(cmdstack): file_out = None file_err = None # If there's at least one command prior to this, need to receive the stdout from the prior command # at the stdin of this command; otherwise, nothing to receive if index > 0: handle_in = _processes[index - 1].stdout else: handle_in = None # If this is not the last command, then stdout needs to be piped to the next command; # otherwise, write stdout to a temporary file so that the contents can be read later if index < len(cmdstack) - 1: handle_out = subprocess.PIPE else: file_out = tempfile.TemporaryFile() handle_out = file_out.fileno() # If we're in debug / info mode, the contents of stderr will be read and printed to the terminal # as the command progresses, hence this needs to go to a pipe; otherwise, write it to a temporary # file so that the contents can be read later if app.verbosity > 1: handle_err = subprocess.PIPE else: file_err = tempfile.TemporaryFile() handle_err = file_err.fileno() # Set off the processes try: try: process = subprocess.Popen(to_execute, stdin=handle_in, stdout=handle_out, stderr=handle_err, env=_env, preexec_fn=os.setpgrp) # pylint: disable=bad-option-value,subprocess-popen-preexec-fn except AttributeError: process = subprocess.Popen(to_execute, stdin=handle_in, stdout=handle_out, stderr=handle_err, env=_env) _processes.append(process) tempfiles.append((file_out, file_err)) # FileNotFoundError not defined in Python 2.7 except OSError as e: if exitOnError: app.error('\'' + to_execute[0] + '\' not executed ("' + str(e) + '"); script cannot proceed') else: app.warn('\'' + to_execute[0] + '\' not executed ("' + str(e) + '")') for p in _processes: p.terminate() _processes = [] break return_stdout = '' return_stderr = '' error = False error_text = '' # Wait for all commands to complete # Switch how we monitor running processes / wait for them to complete # depending on whether or not the user has specified -info or -debug option try: if app.verbosity > 1: for process in _processes: stderrdata = b'' do_indent = True while True: # Have to read one character at a time: Waiting for a newline character using e.g. readline() will prevent MRtrix progressbars from appearing byte = process.stderr.read(1) stderrdata += byte char = byte.decode('cp1252', errors='ignore') if not char and process.poll() is not None: break if do_indent and char in string.printable and char != '\r' and char != '\n': sys.stderr.write(' ') do_indent = False elif char in ['\r', '\n']: do_indent = True sys.stderr.write(char) sys.stderr.flush() stderrdata = stderrdata.decode('utf-8', errors='replace') return_stderr += stderrdata if process.returncode: error = True error_text += stderrdata else: for process in _processes: process.wait() except (KeyboardInterrupt, SystemExit): app.handler(signal.SIGINT, inspect.currentframe()) # Re-enable interrupt signal handler try: signal.signal(signal.SIGINT, app.handler) except: pass # For any command stdout / stderr data that wasn't either passed to another command or # printed to the terminal during execution, read it here. for index in range(len(cmdstack)): if tempfiles[index][0] is not None: tempfiles[index][0].flush() tempfiles[index][0].seek(0) stdout_text = tempfiles[index][0].read().decode('utf-8', errors='replace') return_stdout += stdout_text if _processes[index].returncode: error = True error_text += stdout_text if tempfiles[index][1] is not None: tempfiles[index][1].flush() tempfiles[index][1].seek(0) stderr_text = tempfiles[index][1].read().decode('utf-8', errors='replace') return_stderr += stderr_text if _processes[index].returncode: error = True error_text += stderr_text _processes = [] if error: if exitOnError: app.cleanup = False caller = inspect.getframeinfo(inspect.stack()[1][0]) script_name = os.path.basename(sys.argv[0]) app.console('') try: filename = caller.filename lineno = caller.lineno except AttributeError: filename = caller[1] lineno = caller[2] sys.stderr.write(script_name + ': ' + app.colourError + '[ERROR] Command failed: ' + cmd + app.colourClear + app.colourDebug + ' (' + os.path.basename(filename) + ':' + str(lineno) + ')' + app.colourClear + '\n') sys.stderr.write(script_name + ': ' + app.colourConsole + 'Output of failed command:' + app.colourClear + '\n') for line in error_text.splitlines(): sys.stderr.write(' ' * (len(script_name) + 2) + line + '\n') app.console('') sys.stderr.flush() if app.tempDir: with open(os.path.join(app.tempDir, 'error.txt'), 'w') as outfile: outfile.write(cmd + '\n\n' + error_text + '\n') app.complete() sys.exit(1) else: app.warn('Command failed: ' + cmd) # Only now do we append to the script log, since the command has completed successfully # Note: Writing the command as it was formed as the input to run.command(): # other flags may potentially change if this file is eventually used to resume the script if app.tempDir: with open(os.path.join(app.tempDir, 'log.txt'), 'a') as outfile: outfile.write(cmd + '\n') return (return_stdout, return_stderr)
def command(cmd, exitOnError=True): import inspect, itertools, os, shlex, subprocess, sys, tempfile from distutils.spawn import find_executable from mrtrix3 import app # This is the only global variable that is _modified_ within this function global _processes # Vectorise the command string, preserving anything encased within quotation marks cmdsplit = shlex.split(cmd) if app._lastFile: # Check to see if the last file produced in the previous script execution is # intended to be produced by this command; if it is, this will be the last # command that gets skipped by the -continue option # It's possible that the file might be defined in a '--option=XXX' style argument # It's also possible that the filename in the command string has the file extension omitted for entry in cmdsplit: if entry.startswith('--') and '=' in entry: cmdtotest = entry.split('=')[1] else: cmdtotest = entry filetotest = [ app._lastFile, os.path.splitext(app._lastFile)[0] ] if cmdtotest in filetotest: app.debug('Detected last file \'' + app._lastFile + '\' in command \'' + cmd + '\'; this is the last run.command() / run.function() call that will be skipped') app._lastFile = '' break if app._verbosity: sys.stderr.write(app.colourExec + 'Skipping command:' + app.colourClear + ' ' + cmd + '\n') sys.stderr.flush() return # This splits the command string based on the piping character '|', such that each # individual executable (along with its arguments) appears as its own list # Note that for Python2 support, it is necessary to convert groupby() output from # a generator to a list before it is passed to filter() cmdstack = [ list(g) for k, g in filter(lambda t : t[0], ((k, list(g)) for k, g in itertools.groupby(cmdsplit, lambda s : s is not '|') ) ) ] for line in cmdstack: is_mrtrix_exe = line[0] in _mrtrix_exe_list if is_mrtrix_exe: line[0] = versionMatch(line[0]) if app._nthreads is not None: line.extend( [ '-nthreads', str(app._nthreads) ] ) # Get MRtrix3 binaries to output additional INFO-level information if running in debug mode if app._verbosity == 3: line.append('-info') elif not app._verbosity: line.append('-quiet') else: line[0] = exeName(line[0]) shebang = _shebang(line[0]) if len(shebang): if not is_mrtrix_exe: # If a shebang is found, and this call is therefore invoking an # interpreter, can't rely on the interpreter finding the script # from PATH; need to find the full path ourselves. line[0] = find_executable(line[0]) for item in reversed(shebang): line.insert(0, item) if app._verbosity: sys.stderr.write(app.colourExec + 'Command:' + app.colourClear + ' ' + cmd + '\n') sys.stderr.flush() app.debug('To execute: ' + str(cmdstack)) # Construct temporary text files for holding stdout / stderr contents when appropriate # (One entry per process; each is a tuple containing two entries, each of which is either a # file-like object, or None) tempfiles = [ ] # Execute all processes _processes = [ ] for index, command in enumerate(cmdstack): file_out = None file_err = None # If there's at least one command prior to this, need to receive the stdout from the prior command # at the stdin of this command; otherwise, nothing to receive if index > 0: handle_in = _processes[index-1].stdout else: handle_in = None # If this is not the last command, then stdout needs to be piped to the next command; # otherwise, write stdout to a temporary file so that the contents can be read later if index < len(cmdstack)-1: handle_out = subprocess.PIPE else: file_out = tempfile.TemporaryFile() handle_out = file_out.fileno() # If we're in debug / info mode, the contents of stderr will be read and printed to the terminal # as the command progresses, hence this needs to go to a pipe; otherwise, write it to a temporary # file so that the contents can be read later if app._verbosity > 1: handle_err = subprocess.PIPE else: file_err = tempfile.TemporaryFile() handle_err = file_err.fileno() # Set off the processes try: process = subprocess.Popen (command, stdin=handle_in, stdout=handle_out, stderr=handle_err, env=_env) _processes.append(process) tempfiles.append( ( file_out, file_err ) ) # FileNotFoundError not defined in Python 2.7 except OSError as e: if exitOnError: app.error('\'' + command[0] + '\' not executed ("' + str(e) + '"); script cannot proceed') else: app.warn('\'' + command[0] + '\' not executed ("' + str(e) + '")') for p in _processes: p.terminate() _processes = [ ] break except (KeyboardInterrupt, SystemExit): import inspect, signal app._handler(signal.SIGINT, inspect.currentframe()) return_stdout = '' return_stderr = '' error = False error_text = '' # Wait for all commands to complete try: # Switch how we monitor running processes / wait for them to complete # depending on whether or not the user has specified -verbose or -debug option if app._verbosity > 1: for process in _processes: stderrdata = '' while True: # Have to read one character at a time: Waiting for a newline character using e.g. readline() will prevent MRtrix progressbars from appearing line = process.stderr.read(1).decode('utf-8') sys.stderr.write(line) sys.stderr.flush() stderrdata += line if not line and process.poll() is not None: break return_stderr += stderrdata if process.returncode: error = True error_text += stderrdata else: for process in _processes: process.wait() except (KeyboardInterrupt, SystemExit): import inspect, signal app._handler(signal.SIGINT, inspect.currentframe()) # For any command stdout / stderr data that wasn't either passed to another command or # printed to the terminal during execution, read it here. for index in range(len(cmdstack)): if tempfiles[index][0] is not None: tempfiles[index][0].flush() tempfiles[index][0].seek(0) stdout_text = tempfiles[index][0].read().decode('utf-8') return_stdout += stdout_text if _processes[index].returncode: error = True error_text += stdout_text if tempfiles[index][1] is not None: tempfiles[index][1].flush() tempfiles[index][1].seek(0) stderr_text = tempfiles[index][1].read().decode('utf-8') return_stderr += stderr_text if _processes[index].returncode: error = True error_text += stderr_text _processes = [ ] if (error): app._cleanup = False if exitOnError: caller = inspect.getframeinfo(inspect.stack()[1][0]) app.console('') sys.stderr.write(os.path.basename(sys.argv[0]) + ': ' + app.colourError + '[ERROR] Command failed: ' + cmd + app.colourClear + app.colourDebug + ' (' + os.path.basename(caller.filename) + ':' + str(caller.lineno) + ')' + app.colourClear + '\n') sys.stderr.write(os.path.basename(sys.argv[0]) + ': ' + app.colourConsole + 'Output of failed command:' + app.colourClear + '\n') sys.stderr.write(error_text) sys.stderr.flush() if app._tempDir: with open(os.path.join(app._tempDir, 'error.txt'), 'w') as outfile: outfile.write(cmd + '\n\n' + error_text + '\n') app.complete() sys.exit(1) else: app.warn('Command failed: ' + cmd) # Only now do we append to the script log, since the command has completed successfully # Note: Writing the command as it was formed as the input to run.command(): # other flags may potentially change if this file is eventually used to resume the script if app._tempDir: with open(os.path.join(app._tempDir, 'log.txt'), 'a') as outfile: outfile.write(cmd + '\n') return (return_stdout, return_stderr)
def command(cmd, exitOnError=True): #pylint: disable=unused-variable import inspect, itertools, shlex, signal, string, subprocess, sys, tempfile from distutils.spawn import find_executable from mrtrix3 import app # This is the only global variable that is _modified_ within this function global _processes # Vectorise the command string, preserving anything encased within quotation marks if os.sep == '/': # Cheap POSIX compliance check cmdsplit = shlex.split(cmd) else: # Native Windows Python cmdsplit = [ entry.strip('\"') for entry in shlex.split(cmd, posix=False) ] if _lastFile: if _triggerContinue(cmdsplit): app.debug('Detected last file in command \'' + cmd + '\'; this is the last run.command() / run.function() call that will be skipped') if app.verbosity: sys.stderr.write(app.colourExec + 'Skipping command:' + app.colourClear + ' ' + cmd + '\n') sys.stderr.flush() return ('', '') # This splits the command string based on the piping character '|', such that each # individual executable (along with its arguments) appears as its own list cmdstack = [ list(g) for k, g in itertools.groupby(cmdsplit, lambda s : s != '|') if k ] for line in cmdstack: is_mrtrix_exe = line[0] in _mrtrix_exe_list if is_mrtrix_exe: line[0] = versionMatch(line[0]) if app.numThreads is not None: line.extend( [ '-nthreads', str(app.numThreads) ] ) # Get MRtrix3 binaries to output additional INFO-level information if running in debug mode if app.verbosity == 3: line.append('-info') elif not app.verbosity: line.append('-quiet') else: line[0] = exeName(line[0]) shebang = _shebang(line[0]) if shebang: if not is_mrtrix_exe: # If a shebang is found, and this call is therefore invoking an # interpreter, can't rely on the interpreter finding the script # from PATH; need to find the full path ourselves. line[0] = find_executable(line[0]) for item in reversed(shebang): line.insert(0, item) app.debug('To execute: ' + str(cmdstack)) if app.verbosity: sys.stderr.write(app.colourExec + 'Command:' + app.colourClear + ' ' + cmd + '\n') sys.stderr.flush() # Disable interrupt signal handler while threads are running try: signal.signal(signal.SIGINT, signal.default_int_handler) except: pass # Construct temporary text files for holding stdout / stderr contents when appropriate # (One entry per process; each is a tuple containing two entries, each of which is either a # file-like object, or None) tempfiles = [ ] # Execute all processes assert not _processes for index, to_execute in enumerate(cmdstack): file_out = None file_err = None # If there's at least one command prior to this, need to receive the stdout from the prior command # at the stdin of this command; otherwise, nothing to receive if index > 0: handle_in = _processes[index-1].stdout else: handle_in = None # If this is not the last command, then stdout needs to be piped to the next command; # otherwise, write stdout to a temporary file so that the contents can be read later if index < len(cmdstack)-1: handle_out = subprocess.PIPE else: file_out = tempfile.TemporaryFile() handle_out = file_out.fileno() # If we're in debug / info mode, the contents of stderr will be read and printed to the terminal # as the command progresses, hence this needs to go to a pipe; otherwise, write it to a temporary # file so that the contents can be read later if app.verbosity > 1: handle_err = subprocess.PIPE else: file_err = tempfile.TemporaryFile() handle_err = file_err.fileno() # Set off the processes try: try: process = subprocess.Popen (to_execute, stdin=handle_in, stdout=handle_out, stderr=handle_err, env=_env, preexec_fn=os.setpgrp) # pylint: disable=bad-option-value,subprocess-popen-preexec-fn except AttributeError: process = subprocess.Popen (to_execute, stdin=handle_in, stdout=handle_out, stderr=handle_err, env=_env) _processes.append(process) tempfiles.append( ( file_out, file_err ) ) # FileNotFoundError not defined in Python 2.7 except OSError as e: if exitOnError: app.error('\'' + to_execute[0] + '\' not executed ("' + str(e) + '"); script cannot proceed') else: app.warn('\'' + to_execute[0] + '\' not executed ("' + str(e) + '")') for p in _processes: p.terminate() _processes = [ ] break return_stdout = '' return_stderr = '' error = False error_text = '' # Wait for all commands to complete # Switch how we monitor running processes / wait for them to complete # depending on whether or not the user has specified -info or -debug option try: if app.verbosity > 1: for process in _processes: stderrdata = b'' do_indent = True while True: # Have to read one character at a time: Waiting for a newline character using e.g. readline() will prevent MRtrix progressbars from appearing byte = process.stderr.read(1) stderrdata += byte char = byte.decode('cp1252', errors='ignore') if not char and process.poll() is not None: break if do_indent and char in string.printable and char != '\r' and char != '\n': sys.stderr.write(' ') do_indent = False elif char in [ '\r', '\n' ]: do_indent = True sys.stderr.write(char) sys.stderr.flush() stderrdata = stderrdata.decode('utf-8', errors='replace') return_stderr += stderrdata if process.returncode: error = True error_text += stderrdata else: for process in _processes: process.wait() except (KeyboardInterrupt, SystemExit): app.handler(signal.SIGINT, inspect.currentframe()) # Re-enable interrupt signal handler try: signal.signal(signal.SIGINT, app.handler) except: pass # For any command stdout / stderr data that wasn't either passed to another command or # printed to the terminal during execution, read it here. for index in range(len(cmdstack)): if tempfiles[index][0] is not None: tempfiles[index][0].flush() tempfiles[index][0].seek(0) stdout_text = tempfiles[index][0].read().decode('utf-8', errors='replace') return_stdout += stdout_text if _processes[index].returncode: error = True error_text += stdout_text if tempfiles[index][1] is not None: tempfiles[index][1].flush() tempfiles[index][1].seek(0) stderr_text = tempfiles[index][1].read().decode('utf-8', errors='replace') return_stderr += stderr_text if _processes[index].returncode: error = True error_text += stderr_text _processes = [ ] if error: if exitOnError: app.cleanup = False caller = inspect.getframeinfo(inspect.stack()[1][0]) script_name = os.path.basename(sys.argv[0]) app.console('') try: filename = caller.filename lineno = caller.lineno except AttributeError: filename = caller[1] lineno = caller[2] sys.stderr.write(script_name + ': ' + app.colourError + '[ERROR] Command failed: ' + cmd + app.colourClear + app.colourDebug + ' (' + os.path.basename(filename) + ':' + str(lineno) + ')' + app.colourClear + '\n') sys.stderr.write(script_name + ': ' + app.colourConsole + 'Output of failed command:' + app.colourClear + '\n') for line in error_text.splitlines(): sys.stderr.write(' ' * (len(script_name)+2) + line + '\n') app.console('') sys.stderr.flush() if app.tempDir: with open(os.path.join(app.tempDir, 'error.txt'), 'w') as outfile: outfile.write(cmd + '\n\n' + error_text + '\n') app.complete() sys.exit(1) else: app.warn('Command failed: ' + cmd) # Only now do we append to the script log, since the command has completed successfully # Note: Writing the command as it was formed as the input to run.command(): # other flags may potentially change if this file is eventually used to resume the script if app.tempDir: with open(os.path.join(app.tempDir, 'log.txt'), 'a') as outfile: outfile.write(cmd + '\n') return (return_stdout, return_stderr)
def command(cmd, exitOnError=True): import inspect, itertools, os, shlex, subprocess, sys, tempfile from distutils.spawn import find_executable from mrtrix3 import app global _mrtrix_exe_list # Vectorise the command string, preserving anything encased within quotation marks cmdsplit = shlex.split(cmd) if app._lastFile: # Check to see if the last file produced in the previous script execution is # intended to be produced by this command; if it is, this will be the last # command that gets skipped by the -continue option # It's possible that the file might be defined in a '--option=XXX' style argument # It's also possible that the filename in the command string has the file extension omitted for entry in cmdsplit: if entry.startswith('--') and '=' in entry: cmdtotest = entry.split('=')[1] else: cmdtotest = entry filetotest = [ app._lastFile, os.path.splitext(app._lastFile)[0] ] if cmdtotest in filetotest: app.debug('Detected last file \'' + app._lastFile + '\' in command \'' + cmd + '\'; this is the last run.command() / run.function() call that will be skipped') app._lastFile = '' break if app._verbosity: sys.stderr.write(app.colourExec + 'Skipping command:' + app.colourClear + ' ' + cmd + '\n') sys.stderr.flush() return # This splits the command string based on the piping character '|', such that each # individual executable (along with its arguments) appears as its own list # Note that for Python2 support, it is necessary to convert groupby() output from # a generator to a list before it is passed to filter() cmdstack = [ list(g) for k, g in filter(lambda t : t[0], ((k, list(g)) for k, g in itertools.groupby(cmdsplit, lambda s : s is not '|') ) ) ] for line in cmdstack: is_mrtrix_exe = line[0] in _mrtrix_exe_list if is_mrtrix_exe: line[0] = versionMatch(line[0]) if app._nthreads is not None: line.extend( [ '-nthreads', str(app._nthreads) ] ) # Get MRtrix3 binaries to output additional INFO-level information if running in debug mode if app._verbosity == 3: line.append('-info') elif not app._verbosity: line.append('-quiet') else: line[0] = exeName(line[0]) shebang = _shebang(line[0]) if len(shebang): if not is_mrtrix_exe: # If a shebang is found, and this call is therefore invoking an # interpreter, can't rely on the interpreter finding the script # from PATH; need to find the full path ourselves. line[0] = find_executable(line[0]) for item in reversed(shebang): line.insert(0, item) if app._verbosity: sys.stderr.write(app.colourExec + 'Command:' + app.colourClear + ' ' + cmd + '\n') sys.stderr.flush() app.debug('To execute: ' + str(cmdstack)) # Construct temporary text files for holding stdout / stderr contents when appropriate # (One entry per process; each is a tuple containing two entries, each of which is either a # file-like object, or None) tempfiles = [ ] # Execute all processes _processes = [ ] for index, command in enumerate(cmdstack): file_out = None file_err = None # If there's at least one command prior to this, need to receive the stdout from the prior command # at the stdin of this command; otherwise, nothing to receive if index > 0: handle_in = _processes[index-1].stdout else: handle_in = None # If this is not the last command, then stdout needs to be piped to the next command; # otherwise, write stdout to a temporary file so that the contents can be read later if index < len(cmdstack)-1: handle_out = subprocess.PIPE else: file_out = tempfile.TemporaryFile() handle_out = file_out.fileno() # If we're in debug / info mode, the contents of stderr will be read and printed to the terminal # as the command progresses, hence this needs to go to a pipe; otherwise, write it to a temporary # file so that the contents can be read later if app._verbosity > 1: handle_err = subprocess.PIPE else: file_err = tempfile.TemporaryFile() handle_err = file_err.fileno() # Set off the processes try: process = subprocess.Popen (command, stdin=handle_in, stdout=handle_out, stderr=handle_err) _processes.append(process) tempfiles.append( ( file_out, file_err ) ) # FileNotFoundError not defined in Python 2.7 except OSError as e: if exitOnError: app.error('\'' + command[0] + '\' not executed ("' + str(e) + '"); script cannot proceed') else: app.warn('\'' + command[0] + '\' not executed ("' + str(e) + '")') for p in _processes: p.terminate() _processes = [ ] break except (KeyboardInterrupt, SystemExit): import inspect, signal app._handler(signal.SIGINT, inspect.currentframe()) return_stdout = '' return_stderr = '' error = False error_text = '' # Wait for all commands to complete try: # Switch how we monitor running processes / wait for them to complete # depending on whether or not the user has specified -verbose or -debug option if app._verbosity > 1: for process in _processes: stderrdata = '' while True: # Have to read one character at a time: Waiting for a newline character using e.g. readline() will prevent MRtrix progressbars from appearing line = process.stderr.read(1).decode('utf-8') sys.stderr.write(line) sys.stderr.flush() stderrdata += line if not line and process.poll() is not None: break return_stderr += stderrdata if process.returncode: error = True error_text += stderrdata else: for process in _processes: process.wait() except (KeyboardInterrupt, SystemExit): import inspect, signal app._handler(signal.SIGINT, inspect.currentframe()) # For any command stdout / stderr data that wasn't either passed to another command or # printed to the terminal during execution, read it here. for index in range(len(cmdstack)): if tempfiles[index][0] is not None: tempfiles[index][0].flush() tempfiles[index][0].seek(0) stdout_text = tempfiles[index][0].read().decode('utf-8') return_stdout += stdout_text if _processes[index].returncode: error = True error_text += stdout_text if tempfiles[index][1] is not None: tempfiles[index][1].flush() tempfiles[index][1].seek(0) stderr_text = tempfiles[index][1].read().decode('utf-8') return_stderr += stderr_text if _processes[index].returncode: error = True error_text += stderr_text _processes = [ ] if (error): app._cleanup = False if exitOnError: caller = inspect.getframeinfo(inspect.stack()[1][0]) app.console('') sys.stderr.write(os.path.basename(sys.argv[0]) + ': ' + app.colourError + '[ERROR] Command failed: ' + cmd + app.colourClear + app.colourDebug + ' (' + os.path.basename(caller.filename) + ':' + str(caller.lineno) + ')' + app.colourClear + '\n') sys.stderr.write(os.path.basename(sys.argv[0]) + ': ' + app.colourConsole + 'Output of failed command:' + app.colourClear + '\n') sys.stderr.write(error_text) sys.stderr.flush() if app._tempDir: with open(os.path.join(app._tempDir, 'error.txt'), 'w') as outfile: outfile.write(cmd + '\n\n' + error_text + '\n') app.complete() sys.exit(1) else: app.warn('Command failed: ' + cmd) # Only now do we append to the script log, since the command has completed successfully # Note: Writing the command as it was formed as the input to run.command(): # other flags may potentially change if this file is eventually used to resume the script if app._tempDir: with open(os.path.join(app._tempDir, 'log.txt'), 'a') as outfile: outfile.write(cmd + '\n') return (return_stdout, return_stderr)