| Home | Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: latin-1 -*-
2
3 """This module encapsulates mime operations.
4 """
5 #=======================================================================================
6 # $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/client/pycommon/gmMimeLib.py,v $
7 # $Id: gmMimeLib.py,v 1.27 2010-01-03 18:15:17 ncq Exp $
8 __version__ = "$Revision: 1.27 $"
9 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
10 __license__ = "GPL"
11
12 # stdlib
13 import os, mailcap, sys, mimetypes, shutil, logging
14
15
16 # GNUmed
17 if __name__ == '__main__':
18 sys.path.insert(0, '../../')
19 import gmShellAPI, gmTools, gmCfg2
20
21
22 _log = logging.getLogger('gm.docs')
23 _log.info(__version__)
24 #=======================================================================================
26 """Guess mime type of arbitrary file.
27
28 filenames are supposed to be in Unicode
29 """
30 worst_case = "application/octet-stream"
31
32 # 1) use Python libextractor
33 try:
34 import extractor
35 xtract = extractor.Extractor()
36 props = xtract.extract(filename = aFileName)
37 for prop, val in props:
38 if (prop == 'mimetype') and (val != worst_case):
39 return val
40 except ImportError:
41 _log.exception('Python wrapper for libextractor not installed.')
42
43 ret_code = -1
44
45 # 2) use "file" system command
46 # -i get mime type
47 # -b don't display a header
48 mime_guesser_cmd = u'file -i -b "%s"' % aFileName
49 # this only works on POSIX with 'file' installed (which is standard, however)
50 # it might work on Cygwin installations
51 aPipe = os.popen(mime_guesser_cmd.encode(sys.getfilesystemencoding()), 'r')
52 if aPipe is None:
53 _log.debug("cannot open pipe to [%s]" % mime_guesser_cmd)
54 else:
55 pipe_output = aPipe.readline().replace('\n', '').strip()
56 ret_code = aPipe.close()
57 if ret_code is None:
58 _log.debug('[%s]: <%s>' % (mime_guesser_cmd, pipe_output))
59 if pipe_output not in [u'', worst_case]:
60 return pipe_output
61 else:
62 _log.error('[%s] on %s (%s): failed with exit(%s)' % (mime_guesser_cmd, os.name, sys.platform, ret_code))
63
64 # 3) use "extract" shell level libextractor wrapper
65 mime_guesser_cmd = 'extract -p mimetype "%s"' % aFileName
66 aPipe = os.popen(mime_guesser_cmd.encode(sys.getfilesystemencoding()), 'r')
67 if aPipe is None:
68 _log.debug("cannot open pipe to [%s]" % mime_guesser_cmd)
69 else:
70 pipe_output = aPipe.readline()[11:].replace('\n', '').strip()
71 ret_code = aPipe.close()
72 if ret_code is None:
73 _log.debug('[%s]: <%s>' % (mime_guesser_cmd, pipe_output))
74 if pipe_output not in [u'', worst_case]:
75 return pipe_output
76 else:
77 _log.error('[%s] on %s (%s): failed with exit(%s)' % (mime_guesser_cmd, os.name, sys.platform, ret_code))
78
79 # If we and up here we either have an insufficient systemwide
80 # magic number file or we suffer from a deficient operating system
81 # alltogether. It can't get much worse if we try ourselves.
82
83 _log.info("OS level mime detection failed, falling back to built-in magic")
84
85 import gmMimeMagic
86 mime_type = gmTools.coalesce(gmMimeMagic.file(aFileName), worst_case)
87 del gmMimeMagic
88
89 _log.debug('"%s" -> <%s>' % (aFileName, mime_type))
90 return mime_type
91 #-----------------------------------------------------------------------------------
93 """Return command for viewer for this mime type complete with this file"""
94
95 if aFileName is None:
96 _log.error("You should specify a file name for the replacement of %s.")
97 # last resort: if no file name given replace %s in original with literal '%s'
98 # and hope for the best - we certainly don't want the module default "/dev/null"
99 aFileName = """%s"""
100
101 mailcaps = mailcap.getcaps()
102 (viewer, junk) = mailcap.findmatch(mailcaps, aMimeType, key = 'view', filename = '%s' % aFileName)
103 # FIXME: we should check for "x-token" flags
104
105 _log.debug("<%s> viewer: [%s]" % (aMimeType, viewer))
106
107 return viewer
108 #-----------------------------------------------------------------------------------
110
111 if filename is None:
112 _log.error("You should specify a file name for the replacement of %s.")
113 # last resort: if no file name given replace %s in original with literal '%s'
114 # and hope for the best - we certainly don't want the module default "/dev/null"
115 filename = """%s"""
116
117 mailcaps = mailcap.getcaps()
118 (editor, junk) = mailcap.findmatch(mailcaps, mimetype, key = 'edit', filename = '%s' % filename)
119
120 # FIXME: we should check for "x-token" flags
121
122 _log.debug("<%s> editor: [%s]" % (mimetype, editor))
123
124 return editor
125 #-----------------------------------------------------------------------------------
127 """Return file extension based on what the OS thinks a file of this mimetype should end in."""
128
129 # ask system first
130 ext = mimetypes.guess_extension(mimetype)
131 if ext is not None:
132 _log.debug('<%s>: *.%s' % (mimetype, ext))
133 return ext
134
135 _log.error("<%s>: no suitable file extension known to the OS" % mimetype)
136
137 # try to help the OS a bit
138 cfg = gmCfg2.gmCfgData()
139 ext = cfg.get (
140 group = u'extensions',
141 option = mimetype,
142 source_order = [('user-mime', 'return'), ('system-mime', 'return')]
143 )
144
145 if ext is not None:
146 _log.debug('<%s>: *.%s (%s)' % (mimetype, ext, candidate))
147 return ext
148
149 _log.error("<%s>: no suitable file extension found in config files" % mimetype)
150
151 return ext
152 #-----------------------------------------------------------------------------------
154 if aFile is None:
155 return None
156
157 (path_name, f_ext) = os.path.splitext(aFile)
158 if f_ext != '':
159 return f_ext
160
161 # try to guess one
162 mime_type = guess_mimetype(aFile)
163 f_ext = guess_ext_by_mimetype(mime_type)
164 if f_ext is None:
165 _log.error('unable to guess file extension for mime type [%s]' % mime_type)
166 return None
167
168 return f_ext
169 #-----------------------------------------------------------------------------------
170 _system_startfile_cmd = None
171
172 open_cmds = {
173 'xdg-open': 'xdg-open "%s"', # nascent standard on Linux
174 'kfmclient': 'kfmclient exec "%s"', # KDE
175 'gnome-open': 'gnome-open "%s"', # GNOME
176 'exo-open': 'exo-open "%s"',
177 'op': 'op "%s"',
178 'open': 'open "%s"' # MacOSX: "open -a AppName file" (-a allows to override the default app for the file type)
179 #'run-mailcap'
180 #'explorer'
181 }
182
184
185 global _system_startfile_cmd
186
187 if _system_startfile_cmd == u'':
188 return False, None
189
190 if _system_startfile_cmd is not None:
191 return True, _system_startfile_cmd % filename
192
193 open_cmd_candidates = ['xdg-open', 'kfmclient', 'gnome-open', 'exo-open', 'op', 'open']
194
195 for candidate in open_cmd_candidates:
196 found, binary = gmShellAPI.detect_external_binary(binary = candidate)
197 if not found:
198 continue
199 _system_startfile_cmd = open_cmds[candidate]
200 _log.info('detected local startfile cmd: [%s]', _system_startfile_cmd)
201 return True, _system_startfile_cmd % filename
202
203 _system_startfile_cmd = u''
204 return False, None
205 #-----------------------------------------------------------------------------------
207 """Try to find an appropriate viewer with all tricks and call it.
208
209 block: try to detach from viewer or not, None means to use mailcap default
210 """
211 # does this file exist, actually ?
212 try:
213 open(aFile).close()
214 except:
215 _log.exception('cannot read [%s]', aFile)
216 msg = _('[%s] is not a readable file') % aFile
217 return False, msg
218
219 # try to detect any of the UNIX openers
220 found, startfile_cmd = _get_system_startfile_cmd(aFile)
221 if found:
222 if gmShellAPI.run_command_in_shell(command = startfile_cmd, blocking = block):
223 return True, ''
224
225 mime_type = guess_mimetype(aFile)
226 viewer_cmd = get_viewer_cmd(mime_type, aFile)
227
228 if viewer_cmd is not None:
229 if gmShellAPI.run_command_in_shell(command=viewer_cmd, blocking=block):
230 return True, ''
231
232 _log.warning("no viewer found via standard mailcap system")
233 if os.name == "posix":
234 _log.warning("you should add a viewer for this mime type to your mailcap file")
235 _log.info("let's see what the OS can do about that")
236
237 # does the file already have an extension ?
238 (path_name, f_ext) = os.path.splitext(aFile)
239 # no
240 if f_ext in ['', '.tmp']:
241 # try to guess one
242 f_ext = guess_ext_by_mimetype(mime_type)
243 if f_ext is None:
244 _log.warning("no suitable file extension found, trying anyway")
245 file_to_display = aFile
246 f_ext = '?unknown?'
247 else:
248 file_to_display = aFile + f_ext
249 shutil.copyfile(aFile, file_to_display)
250 # yes
251 else:
252 file_to_display = aFile
253
254 file_to_display = os.path.normpath(file_to_display)
255 _log.debug("file %s <type %s> (ext %s) -> file %s" % (aFile, mime_type, f_ext, file_to_display))
256
257 try:
258 os.startfile(file_to_display)
259 except:
260 _log.exception('os.startfile(%s) failed', file_to_display)
261 msg = _("Unable to display the file:\n\n"
262 " [%s]\n\n"
263 "Your system does not seem to have a (working)\n"
264 "viewer registered for the file type\n"
265 " [%s]"
266 ) % (file_to_display, mime_type)
267 return False, msg
268
269 # don't kill the file from under the (possibly async) viewer
270 # if file_to_display != aFile:
271 # os.remove(file_to_display)
272
273 return True, ''
274 #=======================================================================================
275 if __name__ == "__main__":
276
277 if len(sys.argv) > 1 and sys.argv[1] == u'test':
278
279 filename = sys.argv[2]
280
281 _get_system_startfile_cmd(filename)
282 print _system_startfile_cmd
283 #print guess_mimetype(filename)
284 #print get_viewer_cmd(guess_mimetype(filename), filename)
285 #print guess_ext_by_mimetype(mimetype=filename)
286
287 #=======================================================================================
288 # $Log: gmMimeLib.py,v $
289 # Revision 1.27 2010-01-03 18:15:17 ncq
290 # - get-editor-cmd
291 #
292 # Revision 1.26 2009/11/24 20:48:15 ncq
293 # - quote open command arg
294 #
295 # Revision 1.25 2009/09/17 21:52:40 ncq
296 # - properly log exceptions
297 #
298 # Revision 1.24 2009/09/01 22:24:09 ncq
299 # - document MacOSX open behaviour
300 #
301 # Revision 1.23 2008/12/01 12:12:37 ncq
302 # - turn file-open candidates into list so we can influence detection order
303 #
304 # Revision 1.22 2008/07/22 13:54:25 ncq
305 # - xdg-open is intended to become the standard so look for that first
306 # - some cleanup
307 #
308 # Revision 1.21 2008/03/11 16:58:11 ncq
309 # - much improved detection of startfile cmd under UNIX
310 #
311 # Revision 1.20 2008/01/14 20:28:21 ncq
312 # - use detect_external_binary()
313 #
314 # Revision 1.19 2008/01/11 16:11:40 ncq
315 # - support gnome-open just like kfmclient
316 #
317 # Revision 1.18 2008/01/05 16:38:56 ncq
318 # - eventually use python libextractor module if available
319 # - do not assume every POSIX system knows mailcap, MacOSX doesn't
320 #
321 # Revision 1.17 2007/12/23 11:58:50 ncq
322 # - use gmCfg2
323 #
324 # Revision 1.16 2007/12/12 16:17:15 ncq
325 # - better logger names
326 #
327 # Revision 1.15 2007/12/11 14:31:12 ncq
328 # - use std logging
329 #
330 # Revision 1.14 2007/10/12 14:19:18 ncq
331 # - if file ext is ".tmp" and we were unable to run a viewer on that
332 # file - try to replace the extension based on the mime type
333 #
334 # Revision 1.13 2007/08/31 23:04:04 ncq
335 # - on KDE support kfmclient
336 #
337 # Revision 1.12 2007/08/08 21:23:20 ncq
338 # - improve wording
339 #
340 # Revision 1.11 2007/08/07 21:40:36 ncq
341 # - streamline code
342 # - teach guess_ext_by_mimetype() about mime_type2file_name.conf
343 #
344 # Revision 1.10 2007/07/09 12:39:36 ncq
345 # - cleanup, improved logging
346 #
347 # Revision 1.9 2007/03/31 21:20:14 ncq
348 # - os.popen() needs encoded command strings
349 # - fix test suite
350 #
351 # Revision 1.8 2006/12/23 15:24:28 ncq
352 # - use gmShellAPI
353 #
354 # Revision 1.7 2006/10/31 17:19:26 ncq
355 # - some ERRORs are really WARNings
356 #
357 # Revision 1.6 2006/09/12 17:23:30 ncq
358 # - add block argument to call_viewer_on_file()
359 # - improve file access checks and raise exception on failure
360 # - improve some error messages
361 #
362 # Revision 1.5 2006/06/17 13:15:10 shilbert
363 # - shutil import was added to make it work on Windows
364 #
365 # Revision 1.4 2006/05/16 15:50:51 ncq
366 # - properly escape filename
367 #
368 # Revision 1.3 2006/05/01 18:47:16 ncq
369 # - add use of "extract" command in mimetype guessing
370 #
371 # Revision 1.2 2004/10/11 19:08:08 ncq
372 # - guess_ext_for_file()
373 #
374 # Revision 1.1 2004/02/25 09:30:13 ncq
375 # - moved here from python-common
376 #
377 # Revision 1.5 2003/11/17 10:56:36 sjtan
378 #
379 # synced and commiting.
380 #
381 # Revision 1.1 2003/10/23 06:02:39 sjtan
382 #
383 # manual edit areas modelled after r.terry's specs.
384 #
385 # Revision 1.4 2003/06/26 21:34:43 ncq
386 # - fatal->verbose
387 #
388 # Revision 1.3 2003/04/20 15:33:03 ncq
389 # - call_viewer_on_file() belongs here, I guess
390 #
391 # Revision 1.2 2003/02/17 16:17:20 ncq
392 # - fix typo
393 #
394 # Revision 1.1 2003/02/14 00:22:17 ncq
395 # - mime ops for general use
396 #
397
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 25 03:58:24 2012 | http://epydoc.sourceforge.net |