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

Source Code for Module Gnumed.pycommon.gmMimeLib

  1  # -*- coding: latin-1 -*- 
  2   
  3  """This module encapsulates mime operations. 
  4  """ 
  5  #======================================================================================= 
  6  __version__ = "$Revision: 1.27 $" 
  7  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
  8  __license__ = "GPL" 
  9   
 10  # stdlib 
 11  import sys 
 12  import os 
 13  import mailcap 
 14  import mimetypes 
 15  import subprocess 
 16  import shutil 
 17  import logging 
 18   
 19   
 20  # GNUmed 
 21  if __name__ == '__main__': 
 22          sys.path.insert(0, '../../') 
 23  import gmShellAPI, gmTools, gmCfg2 
 24   
 25   
 26  _log = logging.getLogger('gm.docs') 
 27  _log.info(__version__) 
 28  #======================================================================================= 
29 -def guess_mimetype(aFileName = None):
30 """Guess mime type of arbitrary file. 31 32 filenames are supposed to be in Unicode 33 """ 34 worst_case = "application/octet-stream" 35 36 # 1) use Python libextractor 37 try: 38 import extractor 39 xtract = extractor.Extractor() 40 props = xtract.extract(filename = aFileName) 41 for prop, val in props: 42 if (prop == 'mimetype') and (val != worst_case): 43 return val 44 except ImportError: 45 _log.exception('Python wrapper for libextractor not installed.') 46 47 ret_code = -1 48 49 # 2) use "file" system command 50 # -i get mime type 51 # -b don't display a header 52 mime_guesser_cmd = u'file -i -b "%s"' % aFileName 53 # this only works on POSIX with 'file' installed (which is standard, however) 54 # it might work on Cygwin installations 55 aPipe = os.popen(mime_guesser_cmd.encode(sys.getfilesystemencoding()), 'r') 56 if aPipe is None: 57 _log.debug("cannot open pipe to [%s]" % mime_guesser_cmd) 58 else: 59 pipe_output = aPipe.readline().replace('\n', '').strip() 60 ret_code = aPipe.close() 61 if ret_code is None: 62 _log.debug('[%s]: <%s>' % (mime_guesser_cmd, pipe_output)) 63 if pipe_output not in [u'', worst_case]: 64 return pipe_output 65 else: 66 _log.error('[%s] on %s (%s): failed with exit(%s)' % (mime_guesser_cmd, os.name, sys.platform, ret_code)) 67 68 # 3) use "extract" shell level libextractor wrapper 69 mime_guesser_cmd = 'extract -p mimetype "%s"' % aFileName 70 aPipe = os.popen(mime_guesser_cmd.encode(sys.getfilesystemencoding()), 'r') 71 if aPipe is None: 72 _log.debug("cannot open pipe to [%s]" % mime_guesser_cmd) 73 else: 74 pipe_output = aPipe.readline()[11:].replace('\n', '').strip() 75 ret_code = aPipe.close() 76 if ret_code is None: 77 _log.debug('[%s]: <%s>' % (mime_guesser_cmd, pipe_output)) 78 if pipe_output not in [u'', worst_case]: 79 return pipe_output 80 else: 81 _log.error('[%s] on %s (%s): failed with exit(%s)' % (mime_guesser_cmd, os.name, sys.platform, ret_code)) 82 83 # If we and up here we either have an insufficient systemwide 84 # magic number file or we suffer from a deficient operating system 85 # alltogether. It can't get much worse if we try ourselves. 86 87 _log.info("OS level mime detection failed, falling back to built-in magic") 88 89 import gmMimeMagic 90 mime_type = gmTools.coalesce(gmMimeMagic.file(aFileName), worst_case) 91 del gmMimeMagic 92 93 _log.debug('"%s" -> <%s>' % (aFileName, mime_type)) 94 return mime_type
95 #-----------------------------------------------------------------------------------
96 -def get_viewer_cmd(aMimeType = None, aFileName = None, aToken = None):
97 """Return command for viewer for this mime type complete with this file""" 98 99 if aFileName is None: 100 _log.error("You should specify a file name for the replacement of %s.") 101 # last resort: if no file name given replace %s in original with literal '%s' 102 # and hope for the best - we certainly don't want the module default "/dev/null" 103 aFileName = """%s""" 104 105 mailcaps = mailcap.getcaps() 106 (viewer, junk) = mailcap.findmatch(mailcaps, aMimeType, key = 'view', filename = '%s' % aFileName) 107 # FIXME: we should check for "x-token" flags 108 109 _log.debug("<%s> viewer: [%s]" % (aMimeType, viewer)) 110 111 return viewer
112 #-----------------------------------------------------------------------------------
113 -def get_editor_cmd(mimetype=None, filename=None):
114 115 if filename is None: 116 _log.error("You should specify a file name for the replacement of %s.") 117 # last resort: if no file name given replace %s in original with literal '%s' 118 # and hope for the best - we certainly don't want the module default "/dev/null" 119 filename = """%s""" 120 121 mailcaps = mailcap.getcaps() 122 (editor, junk) = mailcap.findmatch(mailcaps, mimetype, key = 'edit', filename = '%s' % filename) 123 124 # FIXME: we should check for "x-token" flags 125 126 _log.debug("<%s> editor: [%s]" % (mimetype, editor)) 127 128 return editor
129 #-----------------------------------------------------------------------------------
130 -def guess_ext_by_mimetype(mimetype=''):
131 """Return file extension based on what the OS thinks a file of this mimetype should end in.""" 132 133 # ask system first 134 ext = mimetypes.guess_extension(mimetype) 135 if ext is not None: 136 _log.debug('<%s>: *.%s' % (mimetype, ext)) 137 return ext 138 139 _log.error("<%s>: no suitable file extension known to the OS" % mimetype) 140 141 # try to help the OS a bit 142 cfg = gmCfg2.gmCfgData() 143 ext = cfg.get ( 144 group = u'extensions', 145 option = mimetype, 146 source_order = [('user-mime', 'return'), ('system-mime', 'return')] 147 ) 148 149 if ext is not None: 150 _log.debug('<%s>: *.%s (%s)' % (mimetype, ext, candidate)) 151 return ext 152 153 _log.error("<%s>: no suitable file extension found in config files" % mimetype) 154 155 return ext
156 #-----------------------------------------------------------------------------------
157 -def guess_ext_for_file(aFile=None):
158 if aFile is None: 159 return None 160 161 (path_name, f_ext) = os.path.splitext(aFile) 162 if f_ext != '': 163 return f_ext 164 165 # try to guess one 166 mime_type = guess_mimetype(aFile) 167 f_ext = guess_ext_by_mimetype(mime_type) 168 if f_ext is None: 169 _log.error('unable to guess file extension for mime type [%s]' % mime_type) 170 return None 171 172 return f_ext
173 #----------------------------------------------------------------------------------- 174 _system_startfile_cmd = None 175 176 open_cmds = { 177 'xdg-open': 'xdg-open "%s"', # nascent standard on Linux 178 'kfmclient': 'kfmclient exec "%s"', # KDE 179 'gnome-open': 'gnome-open "%s"', # GNOME 180 'exo-open': 'exo-open "%s"', 181 'op': 'op "%s"', 182 'open': 'open "%s"' # MacOSX: "open -a AppName file" (-a allows to override the default app for the file type) 183 #'run-mailcap' 184 #'explorer' 185 } 186
187 -def _get_system_startfile_cmd(filename):
188 189 global _system_startfile_cmd 190 191 if _system_startfile_cmd == u'': 192 return False, None 193 194 if _system_startfile_cmd is not None: 195 return True, _system_startfile_cmd % filename 196 197 open_cmd_candidates = ['xdg-open', 'kfmclient', 'gnome-open', 'exo-open', 'op', 'open'] 198 199 for candidate in open_cmd_candidates: 200 found, binary = gmShellAPI.detect_external_binary(binary = candidate) 201 if not found: 202 continue 203 _system_startfile_cmd = open_cmds[candidate] 204 _log.info('detected local startfile cmd: [%s]', _system_startfile_cmd) 205 return True, _system_startfile_cmd % filename 206 207 _system_startfile_cmd = u'' 208 return False, None
209 #-----------------------------------------------------------------------------------
210 -def convert_file(filename=None, target_mime=None, target_filename=None, target_extension=None):
211 """Convert file from one format into another. 212 213 target_mime: a mime type 214 """ 215 if target_extension is None: 216 tmp, target_extension = os.path.splitext(target_filename) 217 218 base_name = u'gm-convert_file' 219 220 paths = gmTools.gmPaths() 221 local_script = os.path.join(paths.local_base_dir, '..', 'external-tools', base_name) 222 223 candidates = [ base_name, local_script ] #, base_name + u'.bat' 224 found, binary = gmShellAPI.find_first_binary(binaries = candidates) 225 if not found: 226 binary = base_name# + r'.bat' 227 228 cmd_line = [ 229 binary, 230 filename, 231 target_mime, 232 target_extension.strip('.'), 233 target_filename 234 ] 235 _log.debug('converting: %s', cmd_line) 236 try: 237 gm_convert = subprocess.Popen(cmd_line) 238 except OSError: 239 _log.debug('cannot run <%s(.bat)>', base_name) 240 return False 241 gm_convert.communicate() 242 if gm_convert.returncode != 0: 243 _log.error('<%s(.bat)> returned [%s], failed to convert', base_name, gm_convert.returncode) 244 return False 245 246 return True
247 #-----------------------------------------------------------------------------------
248 -def call_viewer_on_file(aFile = None, block=None):
249 """Try to find an appropriate viewer with all tricks and call it. 250 251 block: try to detach from viewer or not, None means to use mailcap default 252 """ 253 # does this file exist, actually ? 254 try: 255 open(aFile).close() 256 except: 257 _log.exception('cannot read [%s]', aFile) 258 msg = _('[%s] is not a readable file') % aFile 259 return False, msg 260 261 # try to detect any of the UNIX openers 262 found, startfile_cmd = _get_system_startfile_cmd(aFile) 263 if found: 264 if gmShellAPI.run_command_in_shell(command = startfile_cmd, blocking = block): 265 return True, '' 266 267 mime_type = guess_mimetype(aFile) 268 viewer_cmd = get_viewer_cmd(mime_type, aFile) 269 270 if viewer_cmd is not None: 271 if gmShellAPI.run_command_in_shell(command=viewer_cmd, blocking=block): 272 return True, '' 273 274 _log.warning("no viewer found via standard mailcap system") 275 if os.name == "posix": 276 _log.warning("you should add a viewer for this mime type to your mailcap file") 277 _log.info("let's see what the OS can do about that") 278 279 # does the file already have an extension ? 280 (path_name, f_ext) = os.path.splitext(aFile) 281 # no 282 if f_ext in ['', '.tmp']: 283 # try to guess one 284 f_ext = guess_ext_by_mimetype(mime_type) 285 if f_ext is None: 286 _log.warning("no suitable file extension found, trying anyway") 287 file_to_display = aFile 288 f_ext = '?unknown?' 289 else: 290 file_to_display = aFile + f_ext 291 shutil.copyfile(aFile, file_to_display) 292 # yes 293 else: 294 file_to_display = aFile 295 296 file_to_display = os.path.normpath(file_to_display) 297 _log.debug("file %s <type %s> (ext %s) -> file %s" % (aFile, mime_type, f_ext, file_to_display)) 298 299 try: 300 os.startfile(file_to_display) 301 except: 302 _log.exception('os.startfile(%s) failed', file_to_display) 303 msg = _("Unable to display the file:\n\n" 304 " [%s]\n\n" 305 "Your system does not seem to have a (working)\n" 306 "viewer registered for the file type\n" 307 " [%s]" 308 ) % (file_to_display, mime_type) 309 return False, msg 310 311 # don't kill the file from under the (possibly async) viewer 312 # if file_to_display != aFile: 313 # os.remove(file_to_display) 314 315 return True, ''
316 #======================================================================================= 317 if __name__ == "__main__": 318 319 if len(sys.argv) > 1 and sys.argv[1] == u'test': 320 321 filename = sys.argv[2] 322 323 _get_system_startfile_cmd(filename) 324 print _system_startfile_cmd 325 #print guess_mimetype(filename) 326 #print get_viewer_cmd(guess_mimetype(filename), filename) 327 #print guess_ext_by_mimetype(mimetype=filename) 328