Package Gnumed :: Package pycommon :: Module gmShellAPI
[frames] | no frames]

Source Code for Module Gnumed.pycommon.gmShellAPI

  1  __doc__ = """GNUmed general tools.""" 
  2   
  3  #=========================================================================== 
  4  __version__ = "$Revision: 1.13 $" 
  5  __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>" 
  6  __license__ = "GPL (details at http://www.gnu.org)" 
  7   
  8   
  9  # stdlib 
 10  import os, sys, logging 
 11   
 12   
 13  _log = logging.getLogger('gm.shell') 
 14  _log.info(__version__) 
 15   
 16  #=========================================================================== 
17 -def detect_external_binary(binary=None):
18 _log.debug('detecting [%s]', binary) 19 20 # is it a sufficiently qualified path ? 21 if os.access(binary, os.X_OK): 22 return (True, binary) 23 24 # try "which" to find the full path 25 cmd = 'which %s' % binary 26 pipe = os.popen(cmd.encode(sys.getfilesystemencoding()), "r") 27 result = pipe.readline() 28 ret_code = pipe.close() 29 if ret_code is not None: 30 _log.debug('[%s] failed, exit code: %s', cmd, ret_code) 31 else: 32 result = result.strip('\r\n') 33 _log.debug('[%s] returned: %s', cmd, result) 34 # redundant on Linux but apparently necessary on MacOSX 35 if os.access(result, os.X_OK): 36 return (True, result) 37 else: 38 _log.debug('[%s] not detected with "which"', binary) 39 40 # consider "d/m/s/locate" to find the full path 41 42 tmp = binary.lstrip() 43 # to be run by wine ? 44 if tmp.startswith('wine'): 45 46 tmp = tmp[4:].strip().strip('"') 47 48 # "wine /standard/unix/path/to/binary" ? 49 if os.access(tmp, os.R_OK): 50 _log.debug('wine call with UNIX path') 51 return (True, binary) 52 53 # 'wine "drive:\a\windows\path\to\binary"' ? 54 cmd = 'winepath -u "%s"' % tmp 55 pipe = os.popen(cmd.encode(sys.getfilesystemencoding()), "r") 56 result = pipe.readline() 57 ret_code = pipe.close() 58 if ret_code is not None: 59 _log.debug('winepath failed') 60 else: 61 result = result.strip('\r\n') 62 if os.access(result, os.R_OK): 63 _log.debug('wine call with Windows path') 64 return (True, binary) 65 else: 66 _log.warning('"winepath -u %s" returned [%s] but the UNIX path is not verifiable', tmp, result) 67 68 return (False, None)
69 #===========================================================================
70 -def find_first_binary(binaries=None):
71 72 found = False 73 binary = None 74 75 for cmd in binaries: 76 _log.debug('looking for [%s]', cmd) 77 78 if cmd is None: 79 continue 80 81 found, binary = detect_external_binary(binary = cmd) 82 if found: 83 break 84 85 return (found, binary)
86 #===========================================================================
87 -def run_command_in_shell(command=None, blocking=False, acceptable_return_codes=None):
88 """Runs a command in a subshell via standard-C system(). 89 90 <command> 91 The shell command to run including command line options. 92 <blocking> 93 This will make the code *block* until the shell command exits. 94 It will likely only work on UNIX shells where "cmd &" makes sense. 95 """ 96 if acceptable_return_codes is None: 97 acceptable_return_codes = [0] 98 99 _log.debug('shell command >>>%s<<<', command) 100 _log.debug('blocking: %s', blocking) 101 _log.debug('acceptable return codes: %s', str(acceptable_return_codes)) 102 103 # FIXME: command should be checked for shell exploits 104 command = command.strip() 105 106 # what the following hack does is this: the user indicated 107 # whether she wants non-blocking external display of files 108 # - the real way to go about this is to have a non-blocking command 109 # in the line in the mailcap file for the relevant mime types 110 # - as non-blocking may not be desirable when *not* displaying 111 # files from within GNUmed the really right way would be to 112 # add a "test" clause to the non-blocking mailcap entry which 113 # yields true if and only if GNUmed is running 114 # - however, this is cumbersome at best and not supported in 115 # some mailcap implementations 116 # - so we allow the user to attempt some control over the process 117 # from within GNUmed by setting a configuration option 118 # - leaving it None means to use the mailcap default or whatever 119 # was specified in the command itself 120 # - True means: tack " &" onto the shell command if necessary 121 # - False means: remove " &" from the shell command if its there 122 # - all this, of course, only works in shells which support 123 # detaching jobs with " &" (so, most POSIX shells) 124 if blocking is True: 125 if command[-2:] == ' &': 126 command = command[:-2] 127 elif blocking is False: 128 if command[-2:] != ' &': 129 command += ' &' 130 131 _log.info('running shell command >>>%s<<<', command) 132 # FIXME: use subprocess.Popen() 133 ret_val = os.system(command.encode(sys.getfilesystemencoding())) 134 _log.debug('os.system() returned: [%s]', ret_val) 135 136 exited_normally = False 137 138 if not hasattr(os, 'WIFEXITED'): 139 _log.debug('platform does not support exit status differentiation') 140 return exited_normally 141 142 _log.debug('exited via exit(): %s', os.WIFEXITED(ret_val)) 143 if os.WIFEXITED(ret_val): 144 _log.debug('exit code: [%s]', os.WEXITSTATUS(ret_val)) 145 exited_normally = (os.WEXITSTATUS(ret_val) in acceptable_return_codes) 146 _log.debug('normal exit: %s', exited_normally) 147 _log.debug('dumped core: %s', os.WCOREDUMP(ret_val)) 148 _log.debug('stopped by signal: %s', os.WIFSIGNALED(ret_val)) 149 if os.WIFSIGNALED(ret_val): 150 _log.debug('STOP signal was: [%s]', os.STOPSIG(ret_val)) 151 _log.debug('TERM signal was: [%s]', os.TERMSIG(ret_val)) 152 153 return exited_normally
154 #===========================================================================
155 -def run_first_available_in_shell(binaries=None, args=None, blocking=False, run_last_one_anyway=False):
156 157 found, binary = find_first_binary(binaries = binaries) 158 159 if not found: 160 if run_last_one_anyway: 161 binary = binaries[-1] 162 else: 163 _log.warning('cannot find any of: %s', binaries) 164 return False 165 166 return run_command_in_shell(command = '%s %s' % (binary, args), blocking = blocking)
167 #=========================================================================== 168 # main 169 #--------------------------------------------------------------------------- 170 if __name__ == '__main__': 171 172 if len(sys.argv) < 2: 173 sys.exit() 174 175 if sys.argv[1] != u'test': 176 sys.exit() 177 178 logging.basicConfig(level = logging.DEBUG) 179 #---------------------------------------------------------
180 - def test_detect_external_binary():
181 found, path = detect_external_binary(binary = sys.argv[2]) 182 if found: 183 print "found as:", path 184 else: 185 print sys.argv[2], "not found"
186 #---------------------------------------------------------
187 - def test_run_command_in_shell():
188 print "-------------------------------------" 189 print "running:", sys.argv[2] 190 if run_command_in_shell(command=sys.argv[2], blocking=True): 191 print "-------------------------------------" 192 print "success" 193 else: 194 print "-------------------------------------" 195 print "failure, consult log"
196 #--------------------------------------------------------- 197 test_run_command_in_shell() 198 #test_detect_external_binary() 199 200 #=========================================================================== 201