Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
                    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()

Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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)