BASIS  version 1.2.3 (revision 2104)
stdaux.py
00001 ##############################################################################
00002 # @file  stdaux.py
00003 # @brief Standard auxiliary functions.
00004 #
00005 # @note The stdaux.py module is automatically created by BASIS from the
00006 #       template file stdaux.py.in which is part of the BASIS installation.
00007 #
00008 # Copyright (c) 2011 University of Pennsylvania. All rights reserved.<br />
00009 # See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.
00010 #
00011 # Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00012 #
00013 # @ingroup BasisPythonUtilities
00014 ##############################################################################
00015 
00016 """
00017 Standard utility functions.
00018 
00019 Copyright (c) 2011 University of Pennsylvania. All rights reserved.
00020 See COPYING file or https://www.rad.upenn.edu/sbia/software/license.html.
00021 
00022 Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00023 
00024 """
00025 
00026 # ============================================================================
00027 # imports
00028 # ============================================================================
00029 
00030 import sys        # system-specific functionality
00031 import subprocess # execution of external command
00032 import shlex      # used to split() quoted command-line
00033 import re         # to see if string contains whitespaces
00034 
00035 from sbia.basis.executabletargetinfo import get_executable_path
00036 
00037 
00038 ## @addtogroup BasisPythonUtilities
00039 # @{
00040 
00041 
00042 # ============================================================================
00043 # version / contact
00044 # ============================================================================
00045 
00046 # ----------------------------------------------------------------------------
00047 def print_version(name, copyright=None, license=None):
00048     """
00049     Print version information including copyright and license notices.
00050      
00051     @param [in] name      Name of executable. Should not be set programmatically
00052                           to the first argument to the __main__ module.
00053     @param [in] copyright The copyright notice. If None, the official default
00054                           copyright is used without year, which is not desired.
00055                           If an empty string is given, no copyright notice is
00056                           printed.
00057     @param [in] license   Information regarding licensing. If None, the official
00058                           software license of SBIA is used. If an empty string
00059                           is given, no license information is printed.
00060     
00061     """
00062     # version
00063     sys.stdout.write(name + " (BASIS) version 1.2.3 (revision 2104)\n")
00064     # copyright notice
00065     if not copyright:
00066         sys.stdout.write("Copyright (c) University of Pennsylvania. All rights reserved.\n")
00067     elif copyright != "":
00068         sys.stdout.write(copyright)
00069         sys.stdout.write('\n')
00070     # license information
00071     if not license:
00072         sys.stdout.write("See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.\n")
00073     elif license != "":
00074         sys.stdout.write(license)
00075         sys.stdout.write('\n')
00076 
00077 # ----------------------------------------------------------------------------
00078 def print_contact(contact=None):
00079     """
00080     Print contact information.
00081     
00082     @param [in] contact Name of contact. If None, the default contact of SBIA
00083                         is used, which is recommended.
00084     
00085     """
00086     sys.stdout.write("Contact:\n  ")
00087     if not contact:
00088         sys.stdout.write("SBIA Group <sbia-software at uphs.upenn.edu>")
00089     else:
00090         sys.stdout.write(contact)
00091     sys.stdout.write('\n')
00092 
00093 # ============================================================================
00094 # command execution
00095 # ============================================================================
00096 
00097 # ----------------------------------------------------------------------------
00098 class SubprocessError(Exception):
00099     """Exception thrown when command execution failed."""
00100 
00101     def __init__(self, msg):
00102         """Initialize exception, i.e., set message describing failure."""
00103         self._message = msg
00104 
00105     def __str__(self):
00106         """Return string representation of exception message."""
00107         return self._message
00108 
00109 # ----------------------------------------------------------------------------
00110 def execute_process(args, quiet=False, stdout=False, allow_fail=False, verbose=0, simulate=False):
00111     """
00112     Execute command as subprocess.
00113     
00114     @param [in] args       Command with arguments given either as single quoted
00115                            string or array of command name and arguments.
00116                            In the latter case, the array elements are converted
00117                            to strings using the built-in str() function.
00118                            Hence, any type which can be converted to a string
00119                            can be used. The first argument must be the name
00120                            or path of the executable of the command or the
00121                            name of the corresponding build target.
00122     @param [in] quiet      Turns off output of stdout of child process to
00123                            stdout of parent process.
00124     @param [in] stdout     Whether to return the command output.
00125     @param [in] allow_fail If true, does not raise an exception if return
00126                            value is non-zero. Otherwise, a SubprocessError is
00127                            raised by this function.
00128     @param [in] verbose    Verbosity of output messages.
00129                            Does not affect verbosity of executed command.
00130     @param [in] simulate   Whether to simulate command execution only.
00131     
00132     @return Exit code of executed command. If @p stdout is True, a tuple
00133             consisting of the exit code and command output is returned.
00134     
00135     @throws SubprocessError If command execution failed. This exception is not
00136                             raised if the command executed with non-zero exit
00137                             code but @p allow_fail set to True.
00138     
00139     """
00140     # convert args to list of strings
00141     if type(args) is list:
00142         args = [str(i) for i in args]
00143     elif type(args) is str:
00144         args = shlex.split(args)
00145     else:
00146         raise SubprocessError("execute_process(): Argument args must be either list or string")
00147     if len(args) == 0:
00148         raise SubprocessError("execute_process(): No command specified")
00149     # map build target name to executable file path
00150     path = get_executable_path(args[0])
00151     if not path:
00152         raise SubprocessError(args[0] + ': Either unknown build target or command not found')
00153     args[0] = path
00154     # some verbose output
00155     if verbose > 0:
00156         sys.stdout.write('$')
00157         re_whitespace = re.compile('\s');
00158         for arg in args:
00159             sys.stdout.write(' ')
00160             if re_whitespace.search(arg):
00161                 sys.stdout.write('"' + arg + '"')
00162             else:
00163                 sys.stdout.write(arg)
00164         if simulate:
00165             sys.stdout.write(" (simulated)")
00166         sys.stdout.write('\n')
00167     # execute command
00168     status = 0
00169     output = ''
00170     if not simulate:
00171         try:
00172             # open subprocess
00173             process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00174             # read stdout until EOF
00175             for line in process.stdout:
00176                 if stdout:
00177                     output = output + line
00178                 if not quiet:
00179                     print line.rstrip()
00180                     sys.stdout.flush()
00181             # wait until subprocess terminated and set exit code
00182             (out, err) = process.communicate()
00183             # print error messages of subprocess
00184             for line in err: sys.stderr.write(line);
00185             # get exit code
00186             status = process.returncode
00187         except OSError, e:
00188             raise SubprocessError(args[0] + ': ' + str(e))
00189         except Exception, e:
00190             msg  = "Exception while executing \"" + args[0] + "\"!\n"
00191             msg += "\tArguments: " + ' '.join(['"' + i + '"' for i in args[1:]]) + '\n'
00192             msg += '\t' + str(e)
00193             raise SubprocessError(msg)
00194     # if command failed, throw an exception
00195     if status != 0 and not allow_fail:
00196         raise SubprocessError("Command " + ' '.join(['"' + i + '"' for i in args[0:]]) + " failed")
00197     # return
00198     if stdout:
00199         return (status, output)
00200     else:
00201         return status
00202 
00203 
00204 ## @}
00205 # end of Doxygen group