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 
 11  import sys 
 12  import logging 
 13  import subprocess 
 14  import shlex 
 15   
 16   
 17  _log = logging.getLogger('gm.shell') 
 18  _log.info(__version__) 
 19   
 20  #=========================================================================== 
21 -def is_cmd_in_path(cmd=None):
22 23 _log.debug('cmd: [%s]', cmd) 24 dirname = os.path.dirname(cmd) 25 _log.debug('dir: [%s]', dirname) 26 if dirname != u'': 27 _log.info('command with full or relative path, not searching in PATH for binary') 28 return (None, None) 29 30 env_paths = os.environ['PATH'] 31 _log.debug('${PATH}: %s', env_paths) 32 for path in env_paths.split(os.pathsep): 33 candidate = os.path.join(path, cmd) 34 if os.access(candidate, os.X_OK): 35 _log.debug('found [%s]', candidate) 36 return (True, candidate) 37 else: 38 _log.debug('not found: %s', candidate) 39 40 _log.debug('command not found in PATH') 41 42 return (False, None)
43 #===========================================================================
44 -def is_executable_by_wine(cmd=None):
45 46 if not cmd.startswith('wine'): 47 _log.debug('not a WINE call: %s', cmd) 48 return (False, None) 49 50 exe_path = cmd.encode(sys.getfilesystemencoding()) 51 52 exe_path = exe_path[4:].strip().strip('"').strip() 53 # [wine "/standard/unix/path/to/binary.exe"] ? 54 if os.access(exe_path, os.R_OK): 55 _log.debug('WINE call with UNIX path: %s', exe_path) 56 return (True, cmd) 57 58 # detect [winepath] 59 found, full_winepath_path = is_cmd_in_path(cmd = r'winepath') 60 if not found: 61 _log.error('[winepath] not found, cannot check WINE call for Windows path conformance: %s', exe_path) 62 return (False, None) 63 64 # [wine "drive:\a\windows\path\to\binary.exe"] ? 65 cmd_line = r'%s -u "%s"' % ( 66 full_winepath_path.encode(sys.getfilesystemencoding()), 67 exe_path 68 ) 69 _log.debug('converting Windows path to UNIX path: %s' % cmd_line) 70 cmd_line = shlex.split(cmd_line) 71 try: 72 winepath = subprocess.Popen ( 73 cmd_line, 74 stdout = subprocess.PIPE, 75 stderr = subprocess.PIPE, 76 universal_newlines = True 77 ) 78 except OSError: 79 _log.exception('cannot run <winepath>') 80 return (False, None) 81 82 stdout, stderr = winepath.communicate() 83 full_path = stdout.strip('\r\n') 84 _log.debug('UNIX path: %s', full_path) 85 86 if winepath.returncode != 0: 87 _log.error('<winepath -u> returned [%s], failed to convert path', winepath.returncode) 88 return (False, None) 89 90 if os.access(full_path, os.R_OK): 91 _log.debug('WINE call with Windows path') 92 return (True, cmd) 93 94 _log.warning('Windows path [%s] not verifiable under UNIX: %s', exe_path, full_path) 95 return (False, None)
96 #===========================================================================
97 -def detect_external_binary(binary=None):
98 _log.debug('searching for [%s]', binary) 99 100 binary = binary.lstrip() 101 102 # is it a sufficiently qualified, directly usable, explicit path ? 103 if os.access(binary, os.X_OK): 104 _log.debug('found: executable explicit path') 105 return (True, binary) 106 107 # can it be found in PATH ? 108 found, full_path = is_cmd_in_path(cmd = binary) 109 if found: 110 if os.access(full_path, os.X_OK): 111 _log.debug('found: executable in ${PATH}') 112 return (True, full_path) 113 114 # does it seem to be a call via WINE ? 115 is_wine_call, full_path = is_executable_by_wine(cmd = binary) 116 if is_wine_call: 117 _log.debug('found: is valid WINE call') 118 return (True, full_path) 119 120 return (False, None)
121 #===========================================================================
122 -def find_first_binary(binaries=None):
123 124 found = False 125 binary = None 126 127 for cmd in binaries: 128 _log.debug('looking for [%s]', cmd) 129 if cmd is None: 130 continue 131 found, binary = detect_external_binary(binary = cmd) 132 if found: 133 break 134 135 return (found, binary)
136 #===========================================================================
137 -def run_command_in_shell(command=None, blocking=False, acceptable_return_codes=None):
138 """Runs a command in a subshell via standard-C system(). 139 140 <command> 141 The shell command to run including command line options. 142 <blocking> 143 This will make the code *block* until the shell command exits. 144 It will likely only work on UNIX shells where "cmd &" makes sense. 145 """ 146 if acceptable_return_codes is None: 147 acceptable_return_codes = [0] 148 149 _log.debug('shell command >>>%s<<<', command) 150 _log.debug('blocking: %s', blocking) 151 _log.debug('acceptable return codes: %s', str(acceptable_return_codes)) 152 153 # FIXME: command should be checked for shell exploits 154 command = command.strip() 155 156 # what the following hack does is this: the user indicated 157 # whether she wants non-blocking external display of files 158 # - the real way to go about this is to have a non-blocking command 159 # in the line in the mailcap file for the relevant mime types 160 # - as non-blocking may not be desirable when *not* displaying 161 # files from within GNUmed the really right way would be to 162 # add a "test" clause to the non-blocking mailcap entry which 163 # yields true if and only if GNUmed is running 164 # - however, this is cumbersome at best and not supported in 165 # some mailcap implementations 166 # - so we allow the user to attempt some control over the process 167 # from within GNUmed by setting a configuration option 168 # - leaving it None means to use the mailcap default or whatever 169 # was specified in the command itself 170 # - True means: tack " &" onto the shell command if necessary 171 # - False means: remove " &" from the shell command if its there 172 # - all this, of course, only works in shells which support 173 # detaching jobs with " &" (so, most POSIX shells) 174 if blocking is True: 175 if command[-2:] == ' &': 176 command = command[:-2] 177 elif blocking is False: 178 if command[-2:] != ' &': 179 command += ' &' 180 181 _log.info('running shell command >>>%s<<<', command) 182 # FIXME: use subprocess.Popen() 183 ret_val = os.system(command.encode(sys.getfilesystemencoding())) 184 _log.debug('os.system() returned: [%s]', ret_val) 185 186 exited_normally = False 187 188 if not hasattr(os, 'WIFEXITED'): 189 _log.error('platform does not support exit status differentiation') 190 if ret_val in acceptable_return_codes: 191 _log.info('os.system() return value contained in acceptable return codes') 192 _log.info('continuing and hoping for the best') 193 return True 194 return exited_normally 195 196 _log.debug('exited via exit(): %s', os.WIFEXITED(ret_val)) 197 if os.WIFEXITED(ret_val): 198 _log.debug('exit code: [%s]', os.WEXITSTATUS(ret_val)) 199 exited_normally = (os.WEXITSTATUS(ret_val) in acceptable_return_codes) 200 _log.debug('normal exit: %s', exited_normally) 201 _log.debug('dumped core: %s', os.WCOREDUMP(ret_val)) 202 _log.debug('stopped by signal: %s', os.WIFSIGNALED(ret_val)) 203 if os.WIFSIGNALED(ret_val): 204 _log.debug('STOP signal was: [%s]', os.STOPSIG(ret_val)) 205 _log.debug('TERM signal was: [%s]', os.TERMSIG(ret_val)) 206 207 return exited_normally
208 #===========================================================================
209 -def run_first_available_in_shell(binaries=None, args=None, blocking=False, run_last_one_anyway=False, acceptable_return_codes=None):
210 211 found, binary = find_first_binary(binaries = binaries) 212 213 if not found: 214 if run_last_one_anyway: 215 binary = binaries[-1] 216 else: 217 _log.warning('cannot find any of: %s', binaries) 218 return False 219 220 return run_command_in_shell(command = '%s %s' % (binary, args), blocking = blocking, acceptable_return_codes = acceptable_return_codes)
221 #=========================================================================== 222 # main 223 #--------------------------------------------------------------------------- 224 if __name__ == '__main__': 225 226 if len(sys.argv) < 2: 227 sys.exit() 228 229 if sys.argv[1] != u'test': 230 sys.exit() 231 232 logging.basicConfig(level = logging.DEBUG) 233 #---------------------------------------------------------
234 - def test_detect_external_binary():
235 found, path = detect_external_binary(binary = sys.argv[2]) 236 if found: 237 print "found as:", path 238 else: 239 print sys.argv[2], "not found"
240 #---------------------------------------------------------
241 - def test_run_command_in_shell():
242 print "-------------------------------------" 243 print "running:", sys.argv[2] 244 if run_command_in_shell(command=sys.argv[2], blocking=True): 245 print "-------------------------------------" 246 print "success" 247 else: 248 print "-------------------------------------" 249 print "failure, consult log"
250 #---------------------------------------------------------
251 - def test_is_cmd_in_path():
252 print is_cmd_in_path(cmd = sys.argv[2])
253 #---------------------------------------------------------
254 - def test_is_executable_by_wine():
255 print is_executable_by_wine(cmd = sys.argv[2])
256 #--------------------------------------------------------- 257 #test_run_command_in_shell() 258 test_detect_external_binary() 259 #test_is_cmd_in_path() 260 #test_is_executable_by_wine() 261 262 #=========================================================================== 263