0001"""
0002Finds formats given criteria or name.
0003"""
0004
0005import pkg_resources
0006
0007class NoFormatError(LookupError):
0008 """
0009 Raised when no matching format can be found
0010 """
0011
0012__all__ = ['find_format_match', 'get_format',
0013 'NoFormatError']
0014
0015
0016
0017_format_names = {}
0018
0019_format_mimetypes = {}
0020
0021_format_types = {}
0022
0023def find_format_match(type, mimetype):
0024 """
0025 Find a format object that can accept the given mimetype and produce the
0026 given type.
0027
0028 If ``debug`` is true, then information will be logged about how a
0029 format is selected.
0030 """
0031 assert type
0032 assert mimetype
0033 if ';' in mimetype:
0034 mimetype = mimetype.split(';', 1)[0]
0035 try:
0036 value = _format_mimetypes[mimetype][type]
0037 except KeyError:
0038 raise NoFormatError(
0039 "No format found providing %s to %s"
0040 % (mimetype, type))
0041 else:
0042 return _load_ep(value)
0043
0044def find_format_accept(type, accept_list):
0045 """
0046 Find a format that converts the given type to one of the provided
0047 mimetypes in accept_list, choosing whatever the first item in the
0048 list is. Returns (format, content_type)
0049
0050 If force_load is true, then we'll scan for items regardless of whether
0051 they are loaded.
0052 """
0053 assert accept_list, ("Empty accept list passed in")
0054
0055
0056 for mimetype in accept_list:
0057 try:
0058 value = _format_mimetypes[mimetype][type]
0059 except KeyError:
0060 pass
0061 else:
0062 return _load_ep(value), mimetype
0063 raise NoFormatError(
0064 "No format available to convert any of %s to %s"
0065 % (accept_list, type))
0066
0067def find_accept_for_type(type):
0068 """
0069 Return a list of all the mimetypes we know how to convert into the
0070 given Python type
0071 """
0072 return _format_types.get(type, {}).keys()
0073
0074def get_format(name):
0075 """
0076 Gets a format object by name
0077
0078 Formats are named by having an entry point in [httpencode.format]
0079 named ``name <name>``
0080 """
0081 try:
0082 value = _format_names[name]
0083 except KeyError:
0084 raise NoFormatError(
0085 "No format found by the name %r" % name)
0086 else:
0087 return _load_ep(value)
0088
0089def find_format_by_type(type, mimetypes):
0090 """
0091 Finds a format by its type, prefering the mimetypes given (in
0092 order).
0093
0094 ``'*'`` can be used as a final mimetype, but if that is ambiguous
0095 it is an error. (That is, if there are different formats that
0096 work with that Python type).
0097 """
0098 if isinstance(mimetypes, basestring):
0099 raise TypeError(
0100 "mimetypes must be a list (not %r)" % mimetypes)
0101 possible = _format_types.get(type)
0102 if not possible:
0103 raise NoFormatError(
0104 "No formats available for type %r" % type)
0105 for mimetype in mimetypes:
0106 if mimetype == '*':
0107 found = None
0108 for data in possible.values():
0109 if found is None:
0110 found = data
0111 elif found != data:
0112 raise TypeError(
0113 "There are multiple formats that can serialize "
0114 "the type %r (at least %r and %r)"
0115 % (type, found, data))
0116
0117
0118 return _load_ep(found)
0119 if mimetype in possible:
0120 return _load_ep(possible[mimetype])
0121 raise NoFormatError(
0122 "There is not format that serializes the type %r "
0123 "and produces any of the mimetypes %r"
0124 % (type, mimetypes))
0125
0126def _load_ep(data):
0127
0128 dist = pkg_resources.get_distribution(data[0])
0129 return dist.load_entry_point(
0130 'httpencode.format', data[1])
0131
0132def _dist_activated(dist):
0133 entries = dist.get_entry_map('httpencode.format')
0134 for name in entries:
0135 data = (dist.key, name)
0136 if name.startswith('name '):
0137 format_name = name[5:].strip()
0138 if _format_names.get(format_name, data) != data:
0139 raise pkg_resources.VersionConflict(
0140 "Distribution %r has identical format name (%r) as distribution %r"
0141 % (dist, format_name, _format_names[format_nmame][0]))
0142 _format_names[format_name] = data
0143 continue
0144 parts = name.split()
0145 if len(parts) != 3 or parts[1] != 'to':
0146 warnings.warn(
0147 'Entry point [httpencode.format] %r in distribution '
0148 '%r is not a valid format'
0149 % (name, dist))
0150 continue
0151 mimetype, type = parts[0], parts[2]
0152 mdict = _format_mimetypes.setdefault(mimetype, {})
0153 if mdict.get(type, data) != data:
0154 raise pkg_resources.VersionConflict(
0155 "Distribution %r has an identical conversion (%r) as distribution %r"
0156 % (dist, name, mdict[type][0]))
0157 mdict[type] = data
0158 tdict = _format_types.setdefault(type, {})
0159
0160
0161 assert tdict.get(mimetype, data) == data
0162 tdict[mimetype] = data
0163
0164
0165pkg_resources.add_activation_listener(_dist_activated)