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

Source Code for Module Gnumed.pycommon.gmShellAPI

  1  __doc__ = """GNUmed general tools.""" 
  2   
  3  #=========================================================================== 
  4  # $Id: gmShellAPI.py,v 1.13 2010/01/11 22:03:08 ncq Exp $ 
  5  # $Source: /cvsroot/gnumed/gnumed/gnumed/client/pycommon/gmShellAPI.py,v $ 
  6  __version__ = "$Revision: 1.13 $" 
  7  __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>" 
  8  __license__ = "GPL (details at http://www.gnu.org)" 
  9   
 10   
 11  # stdlib 
 12  import os, sys, logging 
 13   
 14   
 15  _log = logging.getLogger('gm.shell') 
 16  _log.info(__version__) 
 17   
 18  #=========================================================================== 
19 -def detect_external_binary(binary=None):
20 _log.debug('detecting [%s]', binary) 21 22 # is it a sufficiently qualified path ? 23 if os.access(binary, os.X_OK): 24 return (True, binary) 25 26 # maybe we are on UNIX and should use "which" to find the full path ? 27 cmd = 'which %s' % binary 28 pipe = os.popen(cmd.encode(sys.getfilesystemencoding()), "r") 29 result = pipe.readline() 30 ret_code = pipe.close() 31 if ret_code is not None: 32 _log.debug('[%s] failed, exit code: %s', cmd, ret_code) 33 else: 34 result = result.strip('\r\n') 35 _log.debug('[%s] returned: %s', cmd, result) 36 # redundant on Linux but apparently necessary on MacOSX 37 if os.access(result, os.X_OK): 38 return (True, result) 39 else: 40 _log.debug('[%s] not detected with "which"', binary) 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 found, binary = detect_external_binary(binary = cmd) 77 if found: 78 break 79 80 return (found, binary)
81 #===========================================================================
82 -def run_command_in_shell(command=None, blocking=False):
83 """Runs a command in a subshell via standard-C system(). 84 85 <command> 86 The shell command to run including command line options. 87 <blocking> 88 This will make the code *block* until the shell command exits. 89 It will likely only work on UNIX shells where "cmd &" makes sense. 90 """ 91 _log.debug('shell command >>>%s<<<', command) 92 _log.debug('blocking: %s', blocking) 93 94 # FIXME: command should be checked for shell exploits 95 command = command.strip() 96 97 # what the following hack does is this: the user indicated 98 # whether she wants non-blocking external display of files 99 # - the real way to go about this is to have a non-blocking command 100 # in the line in the mailcap file for the relevant mime types 101 # - as non-blocking may not be desirable when *not* displaying 102 # files from within GNUmed the really right way would be to 103 # add a "test" clause to the non-blocking mailcap entry which 104 # yields true if and only if GNUmed is running 105 # - however, this is cumbersome at best and not supported in 106 # some mailcap implementations 107 # - so we allow the user to attempt some control over the process 108 # from within GNUmed by setting a configuration option 109 # - leaving it None means to use the mailcap default or whatever 110 # was specified in the command itself 111 # - True means: tack " &" onto the shell command if necessary 112 # - False means: remove " &" from the shell command if its there 113 # - all this, of course, only works in shells which support 114 # detaching jobs with " &" (so, most POSIX shells) 115 if blocking is True: 116 if command[-2:] == ' &': 117 command = command[:-2] 118 elif blocking is False: 119 if command[-2:] != ' &': 120 command += ' &' 121 122 _log.info('running shell command >>>%s<<<', command) 123 # FIXME: use subprocess.Popen() 124 ret_val = os.system(command.encode(sys.getfilesystemencoding())) 125 _log.debug('os.system() returned: [%s]', ret_val) 126 127 exited_normally = False 128 _log.debug('exited via exit(): %s', os.WIFEXITED(ret_val)) 129 if os.WIFEXITED(ret_val): 130 _log.debug('exit code: [%s]', os.WEXITSTATUS(ret_val)) 131 exited_normally = (os.WEXITSTATUS(ret_val) == 0) 132 _log.debug('normal exit: %s', exited_normally) 133 _log.debug('dumped core: %s', os.WCOREDUMP(ret_val)) 134 _log.debug('stopped by signal: %s', os.WIFSIGNALED(ret_val)) 135 if os.WIFSIGNALED(ret_val): 136 _log.debug('STOP signal was: [%s]', os.STOPSIG(ret_val)) 137 _log.debug('TERM signal was: [%s]', os.TERMSIG(ret_val)) 138 139 return exited_normally
140 #===========================================================================
141 -def run_first_available_in_shell(binaries=None, args=None, blocking=False, run_last_one_anyway=False):
142 143 found, binary = find_first_binary(binaries = binaries) 144 145 if not found: 146 if run_last_one_anyway: 147 binary = binaries[-1] 148 else: 149 _log.warning('cannot find any of: %s', binaries) 150 return False 151 152 return run_command_in_shell(command = '%s %s' % (binary, args), blocking=False)
153 #=========================================================================== 154 # main 155 #--------------------------------------------------------------------------- 156 if __name__ == '__main__': 157 158 if len(sys.argv) > 1 and sys.argv[1] == u'test': 159 160 logging.basicConfig(level = logging.DEBUG) 161 162 #---------------------------------------------------------
163 - def test_detect_external_binary():
164 found, path = detect_external_binary(binary = sys.argv[2]) 165 if found: 166 print "found as:", path 167 else: 168 print sys.argv[2], "not found"
169 #---------------------------------------------------------
170 - def test_run_command_in_shell():
171 print "-------------------------------------" 172 print "running:", sys.argv[2] 173 if run_command_in_shell(command=sys.argv[2], blocking=True): 174 print "-------------------------------------" 175 print "success" 176 else: 177 print "-------------------------------------" 178 print "failure, consult log"
179 #--------------------------------------------------------- 180 181 #test_run_command_in_shell() 182 test_detect_external_binary() 183 184 #=========================================================================== 185 # $Log: gmShellAPI.py,v $ 186 # Revision 1.13 2010/01/11 22:03:08 ncq 187 # - comment 188 # 189 # Revision 1.12 2010/01/03 18:16:11 ncq 190 # - find-first-binary 191 # - run-first-available-in-shell 192 # 193 # Revision 1.11 2010/01/01 21:20:01 ncq 194 # - much better logging 195 # 196 # Revision 1.10 2009/04/20 11:39:41 ncq 197 # - properly detect binaries run by Wine 198 # 199 # Revision 1.9 2008/12/09 23:26:12 ncq 200 # - improved logging 201 # 202 # Revision 1.8 2008/03/06 21:25:41 ncq 203 # - optimize detect_external_binary() for the common case 204 # 205 # Revision 1.7 2008/03/06 18:48:21 ncq 206 # - much improved wine-based executable detection 207 # 208 # Revision 1.6 2008/03/02 15:09:35 ncq 209 # - smarten up detect_external_binary about winepath 210 # 211 # Revision 1.5 2008/01/14 20:30:11 ncq 212 # - detect_external_binary() 213 # - better tests 214 # 215 # Revision 1.4 2007/12/12 16:17:16 ncq 216 # - better logger names 217 # 218 # Revision 1.3 2007/12/11 14:33:48 ncq 219 # - use standard logging module 220 # 221 # Revision 1.2 2007/03/31 21:20:34 ncq 222 # - os.system() needs encoded commands 223 # 224 # Revision 1.1 2006/12/23 13:17:32 ncq 225 # - new API 226 # 227