home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Multimedia / k3d-setup-0.7.11.0.exe / lib / mimetypes.py < prev    next >
Encoding:
Python Source  |  2008-01-21  |  18.9 KB  |  537 lines

  1. """Guess the MIME type of a file.
  2.  
  3. This module defines two useful functions:
  4.  
  5. guess_type(url, strict=1) -- guess the MIME type and encoding of a URL.
  6.  
  7. guess_extension(type, strict=1) -- guess the extension for a given MIME type.
  8.  
  9. It also contains the following, for tuning the behavior:
  10.  
  11. Data:
  12.  
  13. knownfiles -- list of files to parse
  14. inited -- flag set when init() has been called
  15. suffix_map -- dictionary mapping suffixes to suffixes
  16. encodings_map -- dictionary mapping suffixes to encodings
  17. types_map -- dictionary mapping suffixes to types
  18.  
  19. Functions:
  20.  
  21. init([files]) -- parse a list of files, default knownfiles
  22. read_mime_types(file) -- parse one file, return a dictionary or None
  23. """
  24.  
  25. import os
  26. import posixpath
  27. import urllib
  28.  
  29. __all__ = [
  30.     "guess_type","guess_extension","guess_all_extensions",
  31.     "add_type","read_mime_types","init"
  32. ]
  33.  
  34. knownfiles = [
  35.     "/etc/mime.types",
  36.     "/etc/httpd/mime.types",                    # Mac OS X
  37.     "/etc/httpd/conf/mime.types",               # Apache
  38.     "/etc/apache/mime.types",                   # Apache 1
  39.     "/etc/apache2/mime.types",                  # Apache 2
  40.     "/usr/local/etc/httpd/conf/mime.types",
  41.     "/usr/local/lib/netscape/mime.types",
  42.     "/usr/local/etc/httpd/conf/mime.types",     # Apache 1.2
  43.     "/usr/local/etc/mime.types",                # Apache 1.3
  44.     ]
  45.  
  46. inited = False
  47.  
  48.  
  49. class MimeTypes:
  50.     """MIME-types datastore.
  51.  
  52.     This datastore can handle information from mime.types-style files
  53.     and supports basic determination of MIME type from a filename or
  54.     URL, and can guess a reasonable extension given a MIME type.
  55.     """
  56.  
  57.     def __init__(self, filenames=(), strict=True):
  58.         if not inited:
  59.             init()
  60.         self.encodings_map = encodings_map.copy()
  61.         self.suffix_map = suffix_map.copy()
  62.         self.types_map = ({}, {}) # dict for (non-strict, strict)
  63.         self.types_map_inv = ({}, {})
  64.         for (ext, type) in types_map.items():
  65.             self.add_type(type, ext, True)
  66.         for (ext, type) in common_types.items():
  67.             self.add_type(type, ext, False)
  68.         for name in filenames:
  69.             self.read(name, strict)
  70.  
  71.     def add_type(self, type, ext, strict=True):
  72.         """Add a mapping between a type and an extension.
  73.  
  74.         When the extension is already known, the new
  75.         type will replace the old one. When the type
  76.         is already known the extension will be added
  77.         to the list of known extensions.
  78.  
  79.         If strict is true, information will be added to
  80.         list of standard types, else to the list of non-standard
  81.         types.
  82.         """
  83.         self.types_map[strict][ext] = type
  84.         exts = self.types_map_inv[strict].setdefault(type, [])
  85.         if ext not in exts:
  86.             exts.append(ext)
  87.  
  88.     def guess_type(self, url, strict=True):
  89.         """Guess the type of a file based on its URL.
  90.  
  91.         Return value is a tuple (type, encoding) where type is None if
  92.         the type can't be guessed (no or unknown suffix) or a string
  93.         of the form type/subtype, usable for a MIME Content-type
  94.         header; and encoding is None for no encoding or the name of
  95.         the program used to encode (e.g. compress or gzip).  The
  96.         mappings are table driven.  Encoding suffixes are case
  97.         sensitive; type suffixes are first tried case sensitive, then
  98.         case insensitive.
  99.  
  100.         The suffixes .tgz, .taz and .tz (case sensitive!) are all
  101.         mapped to '.tar.gz'.  (This is table-driven too, using the
  102.         dictionary suffix_map.)
  103.  
  104.         Optional `strict' argument when False adds a bunch of commonly found,
  105.         but non-standard types.
  106.         """
  107.         scheme, url = urllib.splittype(url)
  108.         if scheme == 'data':
  109.             # syntax of data URLs:
  110.             # dataurl   := "data:" [ mediatype ] [ ";base64" ] "," data
  111.             # mediatype := [ type "/" subtype ] *( ";" parameter )
  112.             # data      := *urlchar
  113.             # parameter := attribute "=" value
  114.             # type/subtype defaults to "text/plain"
  115.             comma = url.find(',')
  116.             if comma < 0:
  117.                 # bad data URL
  118.                 return None, None
  119.             semi = url.find(';', 0, comma)
  120.             if semi >= 0:
  121.                 type = url[:semi]
  122.             else:
  123.                 type = url[:comma]
  124.             if '=' in type or '/' not in type:
  125.                 type = 'text/plain'
  126.             return type, None           # never compressed, so encoding is None
  127.         base, ext = posixpath.splitext(url)
  128.         while ext in self.suffix_map:
  129.             base, ext = posixpath.splitext(base + self.suffix_map[ext])
  130.         if ext in self.encodings_map:
  131.             encoding = self.encodings_map[ext]
  132.             base, ext = posixpath.splitext(base)
  133.         else:
  134.             encoding = None
  135.         types_map = self.types_map[True]
  136.         if ext in types_map:
  137.             return types_map[ext], encoding
  138.         elif ext.lower() in types_map:
  139.             return types_map[ext.lower()], encoding
  140.         elif strict:
  141.             return None, encoding
  142.         types_map = self.types_map[False]
  143.         if ext in types_map:
  144.             return types_map[ext], encoding
  145.         elif ext.lower() in types_map:
  146.             return types_map[ext.lower()], encoding
  147.         else:
  148.             return None, encoding
  149.  
  150.     def guess_all_extensions(self, type, strict=True):
  151.         """Guess the extensions for a file based on its MIME type.
  152.  
  153.         Return value is a list of strings giving the possible filename
  154.         extensions, including the leading dot ('.').  The extension is not
  155.         guaranteed to have been associated with any particular data stream,
  156.         but would be mapped to the MIME type `type' by guess_type().
  157.  
  158.         Optional `strict' argument when false adds a bunch of commonly found,
  159.         but non-standard types.
  160.         """
  161.         type = type.lower()
  162.         extensions = self.types_map_inv[True].get(type, [])
  163.         if not strict:
  164.             for ext in self.types_map_inv[False].get(type, []):
  165.                 if ext not in extensions:
  166.                     extensions.append(ext)
  167.         return extensions
  168.  
  169.     def guess_extension(self, type, strict=True):
  170.         """Guess the extension for a file based on its MIME type.
  171.  
  172.         Return value is a string giving a filename extension,
  173.         including the leading dot ('.').  The extension is not
  174.         guaranteed to have been associated with any particular data
  175.         stream, but would be mapped to the MIME type `type' by
  176.         guess_type().  If no extension can be guessed for `type', None
  177.         is returned.
  178.  
  179.         Optional `strict' argument when false adds a bunch of commonly found,
  180.         but non-standard types.
  181.         """
  182.         extensions = self.guess_all_extensions(type, strict)
  183.         if not extensions:
  184.             return None
  185.         return extensions[0]
  186.  
  187.     def read(self, filename, strict=True):
  188.         """
  189.         Read a single mime.types-format file, specified by pathname.
  190.  
  191.         If strict is true, information will be added to
  192.         list of standard types, else to the list of non-standard
  193.         types.
  194.         """
  195.         fp = open(filename)
  196.         self.readfp(fp, strict)
  197.         fp.close()
  198.  
  199.     def readfp(self, fp, strict=True):
  200.         """
  201.         Read a single mime.types-format file.
  202.  
  203.         If strict is true, information will be added to
  204.         list of standard types, else to the list of non-standard
  205.         types.
  206.         """
  207.         while 1:
  208.             line = fp.readline()
  209.             if not line:
  210.                 break
  211.             words = line.split()
  212.             for i in range(len(words)):
  213.                 if words[i][0] == '#':
  214.                     del words[i:]
  215.                     break
  216.             if not words:
  217.                 continue
  218.             type, suffixes = words[0], words[1:]
  219.             for suff in suffixes:
  220.                 self.add_type(type, '.' + suff, strict)
  221.  
  222. def guess_type(url, strict=True):
  223.     """Guess the type of a file based on its URL.
  224.  
  225.     Return value is a tuple (type, encoding) where type is None if the
  226.     type can't be guessed (no or unknown suffix) or a string of the
  227.     form type/subtype, usable for a MIME Content-type header; and
  228.     encoding is None for no encoding or the name of the program used
  229.     to encode (e.g. compress or gzip).  The mappings are table
  230.     driven.  Encoding suffixes are case sensitive; type suffixes are
  231.     first tried case sensitive, then case insensitive.
  232.  
  233.     The suffixes .tgz, .taz and .tz (case sensitive!) are all mapped
  234.     to ".tar.gz".  (This is table-driven too, using the dictionary
  235.     suffix_map).
  236.  
  237.     Optional `strict' argument when false adds a bunch of commonly found, but
  238.     non-standard types.
  239.     """
  240.     init()
  241.     return guess_type(url, strict)
  242.  
  243.  
  244. def guess_all_extensions(type, strict=True):
  245.     """Guess the extensions for a file based on its MIME type.
  246.  
  247.     Return value is a list of strings giving the possible filename
  248.     extensions, including the leading dot ('.').  The extension is not
  249.     guaranteed to have been associated with any particular data
  250.     stream, but would be mapped to the MIME type `type' by
  251.     guess_type().  If no extension can be guessed for `type', None
  252.     is returned.
  253.  
  254.     Optional `strict' argument when false adds a bunch of commonly found,
  255.     but non-standard types.
  256.     """
  257.     init()
  258.     return guess_all_extensions(type, strict)
  259.  
  260. def guess_extension(type, strict=True):
  261.     """Guess the extension for a file based on its MIME type.
  262.  
  263.     Return value is a string giving a filename extension, including the
  264.     leading dot ('.').  The extension is not guaranteed to have been
  265.     associated with any particular data stream, but would be mapped to the
  266.     MIME type `type' by guess_type().  If no extension can be guessed for
  267.     `type', None is returned.
  268.  
  269.     Optional `strict' argument when false adds a bunch of commonly found,
  270.     but non-standard types.
  271.     """
  272.     init()
  273.     return guess_extension(type, strict)
  274.  
  275. def add_type(type, ext, strict=True):
  276.     """Add a mapping between a type and an extension.
  277.  
  278.     When the extension is already known, the new
  279.     type will replace the old one. When the type
  280.     is already known the extension will be added
  281.     to the list of known extensions.
  282.  
  283.     If strict is true, information will be added to
  284.     list of standard types, else to the list of non-standard
  285.     types.
  286.     """
  287.     init()
  288.     return add_type(type, ext, strict)
  289.  
  290.  
  291. def init(files=None):
  292.     global guess_all_extensions, guess_extension, guess_type
  293.     global suffix_map, types_map, encodings_map, common_types
  294.     global add_type, inited
  295.     inited = True
  296.     db = MimeTypes()
  297.     if files is None:
  298.         files = knownfiles
  299.     for file in files:
  300.         if os.path.isfile(file):
  301.             db.readfp(open(file))
  302.     encodings_map = db.encodings_map
  303.     suffix_map = db.suffix_map
  304.     types_map = db.types_map[True]
  305.     guess_all_extensions = db.guess_all_extensions
  306.     guess_extension = db.guess_extension
  307.     guess_type = db.guess_type
  308.     add_type = db.add_type
  309.     common_types = db.types_map[False]
  310.  
  311.  
  312. def read_mime_types(file):
  313.     try:
  314.         f = open(file)
  315.     except IOError:
  316.         return None
  317.     db = MimeTypes()
  318.     db.readfp(f, True)
  319.     return db.types_map[True]
  320.  
  321.  
  322. def _default_mime_types():
  323.     global suffix_map
  324.     global encodings_map
  325.     global types_map
  326.     global common_types
  327.  
  328.     suffix_map = {
  329.         '.tgz': '.tar.gz',
  330.         '.taz': '.tar.gz',
  331.         '.tz': '.tar.gz',
  332.         '.tbz2': '.tar.bz2',
  333.         }
  334.  
  335.     encodings_map = {
  336.         '.gz': 'gzip',
  337.         '.Z': 'compress',
  338.         '.bz2': 'bzip2',
  339.         }
  340.  
  341.     # Before adding new types, make sure they are either registered with IANA,
  342.     # at http://www.isi.edu/in-notes/iana/assignments/media-types
  343.     # or extensions, i.e. using the x- prefix
  344.  
  345.     # If you add to these, please keep them sorted!
  346.     types_map = {
  347.         '.a'      : 'application/octet-stream',
  348.         '.ai'     : 'application/postscript',
  349.         '.aif'    : 'audio/x-aiff',
  350.         '.aifc'   : 'audio/x-aiff',
  351.         '.aiff'   : 'audio/x-aiff',
  352.         '.au'     : 'audio/basic',
  353.         '.avi'    : 'video/x-msvideo',
  354.         '.bat'    : 'text/plain',
  355.         '.bcpio'  : 'application/x-bcpio',
  356.         '.bin'    : 'application/octet-stream',
  357.         '.bmp'    : 'image/x-ms-bmp',
  358.         '.c'      : 'text/plain',
  359.         # Duplicates :(
  360.         '.cdf'    : 'application/x-cdf',
  361.         '.cdf'    : 'application/x-netcdf',
  362.         '.cpio'   : 'application/x-cpio',
  363.         '.csh'    : 'application/x-csh',
  364.         '.css'    : 'text/css',
  365.         '.dll'    : 'application/octet-stream',
  366.         '.doc'    : 'application/msword',
  367.         '.dot'    : 'application/msword',
  368.         '.dvi'    : 'application/x-dvi',
  369.         '.eml'    : 'message/rfc822',
  370.         '.eps'    : 'application/postscript',
  371.         '.etx'    : 'text/x-setext',
  372.         '.exe'    : 'application/octet-stream',
  373.         '.gif'    : 'image/gif',
  374.         '.gtar'   : 'application/x-gtar',
  375.         '.h'      : 'text/plain',
  376.         '.hdf'    : 'application/x-hdf',
  377.         '.htm'    : 'text/html',
  378.         '.html'   : 'text/html',
  379.         '.ief'    : 'image/ief',
  380.         '.jpe'    : 'image/jpeg',
  381.         '.jpeg'   : 'image/jpeg',
  382.         '.jpg'    : 'image/jpeg',
  383.         '.js'     : 'application/x-javascript',
  384.         '.ksh'    : 'text/plain',
  385.         '.latex'  : 'application/x-latex',
  386.         '.m1v'    : 'video/mpeg',
  387.         '.man'    : 'application/x-troff-man',
  388.         '.me'     : 'application/x-troff-me',
  389.         '.mht'    : 'message/rfc822',
  390.         '.mhtml'  : 'message/rfc822',
  391.         '.mif'    : 'application/x-mif',
  392.         '.mov'    : 'video/quicktime',
  393.         '.movie'  : 'video/x-sgi-movie',
  394.         '.mp2'    : 'audio/mpeg',
  395.         '.mp3'    : 'audio/mpeg',
  396.         '.mp4'    : 'video/mp4',
  397.         '.mpa'    : 'video/mpeg',
  398.         '.mpe'    : 'video/mpeg',
  399.         '.mpeg'   : 'video/mpeg',
  400.         '.mpg'    : 'video/mpeg',
  401.         '.ms'     : 'application/x-troff-ms',
  402.         '.nc'     : 'application/x-netcdf',
  403.         '.nws'    : 'message/rfc822',
  404.         '.o'      : 'application/octet-stream',
  405.         '.obj'    : 'application/octet-stream',
  406.         '.oda'    : 'application/oda',
  407.         '.p12'    : 'application/x-pkcs12',
  408.         '.p7c'    : 'application/pkcs7-mime',
  409.         '.pbm'    : 'image/x-portable-bitmap',
  410.         '.pdf'    : 'application/pdf',
  411.         '.pfx'    : 'application/x-pkcs12',
  412.         '.pgm'    : 'image/x-portable-graymap',
  413.         '.pl'     : 'text/plain',
  414.         '.png'    : 'image/png',
  415.         '.pnm'    : 'image/x-portable-anymap',
  416.         '.pot'    : 'application/vnd.ms-powerpoint',
  417.         '.ppa'    : 'application/vnd.ms-powerpoint',
  418.         '.ppm'    : 'image/x-portable-pixmap',
  419.         '.pps'    : 'application/vnd.ms-powerpoint',
  420.         '.ppt'    : 'application/vnd.ms-powerpoint',
  421.         '.ps'     : 'application/postscript',
  422.         '.pwz'    : 'application/vnd.ms-powerpoint',
  423.         '.py'     : 'text/x-python',
  424.         '.pyc'    : 'application/x-python-code',
  425.         '.pyo'    : 'application/x-python-code',
  426.         '.qt'     : 'video/quicktime',
  427.         '.ra'     : 'audio/x-pn-realaudio',
  428.         '.ram'    : 'application/x-pn-realaudio',
  429.         '.ras'    : 'image/x-cmu-raster',
  430.         '.rdf'    : 'application/xml',
  431.         '.rgb'    : 'image/x-rgb',
  432.         '.roff'   : 'application/x-troff',
  433.         '.rtx'    : 'text/richtext',
  434.         '.sgm'    : 'text/x-sgml',
  435.         '.sgml'   : 'text/x-sgml',
  436.         '.sh'     : 'application/x-sh',
  437.         '.shar'   : 'application/x-shar',
  438.         '.snd'    : 'audio/basic',
  439.         '.so'     : 'application/octet-stream',
  440.         '.src'    : 'application/x-wais-source',
  441.         '.sv4cpio': 'application/x-sv4cpio',
  442.         '.sv4crc' : 'application/x-sv4crc',
  443.         '.swf'    : 'application/x-shockwave-flash',
  444.         '.t'      : 'application/x-troff',
  445.         '.tar'    : 'application/x-tar',
  446.         '.tcl'    : 'application/x-tcl',
  447.         '.tex'    : 'application/x-tex',
  448.         '.texi'   : 'application/x-texinfo',
  449.         '.texinfo': 'application/x-texinfo',
  450.         '.tif'    : 'image/tiff',
  451.         '.tiff'   : 'image/tiff',
  452.         '.tr'     : 'application/x-troff',
  453.         '.tsv'    : 'text/tab-separated-values',
  454.         '.txt'    : 'text/plain',
  455.         '.ustar'  : 'application/x-ustar',
  456.         '.vcf'    : 'text/x-vcard',
  457.         '.wav'    : 'audio/x-wav',
  458.         '.wiz'    : 'application/msword',
  459.         '.wsdl'   : 'application/xml',
  460.         '.xbm'    : 'image/x-xbitmap',
  461.         '.xlb'    : 'application/vnd.ms-excel',
  462.         # Duplicates :(
  463.         '.xls'    : 'application/excel',
  464.         '.xls'    : 'application/vnd.ms-excel',
  465.         '.xml'    : 'text/xml',
  466.         '.xpdl'   : 'application/xml',
  467.         '.xpm'    : 'image/x-xpixmap',
  468.         '.xsl'    : 'application/xml',
  469.         '.xwd'    : 'image/x-xwindowdump',
  470.         '.zip'    : 'application/zip',
  471.         }
  472.  
  473.     # These are non-standard types, commonly found in the wild.  They will
  474.     # only match if strict=0 flag is given to the API methods.
  475.  
  476.     # Please sort these too
  477.     common_types = {
  478.         '.jpg' : 'image/jpg',
  479.         '.mid' : 'audio/midi',
  480.         '.midi': 'audio/midi',
  481.         '.pct' : 'image/pict',
  482.         '.pic' : 'image/pict',
  483.         '.pict': 'image/pict',
  484.         '.rtf' : 'application/rtf',
  485.         '.xul' : 'text/xul'
  486.         }
  487.  
  488.  
  489. _default_mime_types()
  490.  
  491.  
  492. if __name__ == '__main__':
  493.     import sys
  494.     import getopt
  495.  
  496.     USAGE = """\
  497. Usage: mimetypes.py [options] type
  498.  
  499. Options:
  500.     --help / -h       -- print this message and exit
  501.     --lenient / -l    -- additionally search of some common, but non-standard
  502.                          types.
  503.     --extension / -e  -- guess extension instead of type
  504.  
  505. More than one type argument may be given.
  506. """
  507.  
  508.     def usage(code, msg=''):
  509.         print USAGE
  510.         if msg: print msg
  511.         sys.exit(code)
  512.  
  513.     try:
  514.         opts, args = getopt.getopt(sys.argv[1:], 'hle',
  515.                                    ['help', 'lenient', 'extension'])
  516.     except getopt.error, msg:
  517.         usage(1, msg)
  518.  
  519.     strict = 1
  520.     extension = 0
  521.     for opt, arg in opts:
  522.         if opt in ('-h', '--help'):
  523.             usage(0)
  524.         elif opt in ('-l', '--lenient'):
  525.             strict = 0
  526.         elif opt in ('-e', '--extension'):
  527.             extension = 1
  528.     for gtype in args:
  529.         if extension:
  530.             guess = guess_extension(gtype, strict)
  531.             if not guess: print "I don't know anything about type", gtype
  532.             else: print guess
  533.         else:
  534.             guess, encoding = guess_type(gtype, strict)
  535.             if not guess: print "I don't know anything about type", gtype
  536.             else: print 'type:', guess, 'encoding:', encoding
  537.