Remove unused decorators import from soc.views.models.role module.
Patch by: Pawel Solyga
Review by: to-be-reviewed
"""
Parses a variety of ``Accept-*`` headers.
These headers generally take the form of::
value1; q=0.5, value2; q=0
Where the ``q`` parameter is optional. In theory other parameters
exists, but this ignores them.
"""
import re
part_re = re.compile(
r',\s*([^\s;,\n]+)(?:[^,]*?;\s*q=([0-9.]*))?')
def parse_accept(value):
"""
Parses an ``Accept-*`` style header.
A list of ``[(value, quality), ...]`` is returned. ``quality``
will be 1 if it was not given.
"""
result = []
for match in part_re.finditer(','+value):
name = match.group(1)
if name == 'q':
continue
quality = match.group(2) or ''
if not quality:
quality = 1
else:
try:
quality = max(min(float(quality), 1), 0)
except ValueError:
quality = 1
result.append((name, quality))
return result
class Accept(object):
"""
Represents a generic ``Accept-*`` style header.
This object should not be modified. To add items you can use
``accept_obj + 'accept_thing'`` to get a new object
"""
def __init__(self, header_name, header_value):
self.header_name = header_name
self.header_value = header_value
self._parsed = parse_accept(header_value)
def __repr__(self):
return '<%s at %x %s: %s>' % (
self.__class__.__name__,
abs(id(self)),
self.header_name, str(self))
def __str__(self):
result = []
for match, quality in self._parsed:
if quality != 1:
match = '%s;q=%0.1f' % (match, quality)
result.append(match)
return ', '.join(result)
# FIXME: should subtraction be allowed?
def __add__(self, other, reversed=False):
if isinstance(other, Accept):
other = other.header_value
if hasattr(other, 'items'):
other = sorted(other.items(), key=lambda item: -item[1])
if isinstance(other, (list, tuple)):
result = []
for item in other:
if isinstance(item, (list, tuple)):
name, quality = item
result.append('%s; q=%s' % (name, quality))
else:
result.append(item)
other = ', '.join(result)
other = str(other)
my_value = self.header_value
if reversed:
other, my_value = my_value, other
if not other:
new_value = my_value
elif not my_value:
new_value = other
else:
new_value = my_value + ', ' + other
return self.__class__(self.header_name, new_value)
def __radd__(self, other):
return self.__add__(other, True)
def __contains__(self, match):
"""
Returns true if the given object is listed in the accepted
types.
"""
for item, quality in self._parsed:
if self._match(item, match):
return True
def quality(self, match):
"""
Return the quality of the given match. Returns None if there
is no match (not 0).
"""
for item, quality in self._parsed:
if self._match(item, match):
return quality
return None
def first_match(self, matches):
"""
Returns the first match in the sequences of matches that is
allowed. Ignores quality. Returns the first item if nothing
else matches; or if you include None at the end of the match
list then that will be returned.
"""
if not matches:
raise ValueError(
"You must pass in a non-empty list")
for match in matches:
for item, quality in self._parsed:
if self._match(item, match):
return match
if match is None:
return None
return matches[0]
def best_match(self, matches, default_match=None):
"""
Returns the best match in the sequence of matches.
The sequence can be a simple sequence, or you can have
``(match, server_quality)`` items in the sequence. If you
have these tuples then the client quality is multiplied by the
server_quality to get a total.
default_match (default None) is returned if there is no intersection.
"""
best_quality = -1
best_match = default_match
for match_item in matches:
if isinstance(match_item, (tuple, list)):
match, server_quality = match_item
else:
match = match_item
server_quality = 1
for item, quality in self._parsed:
possible_quality = server_quality * quality
if possible_quality < best_quality:
continue
if self._match(item, match):
best_quality = possible_quality
best_match = match
return best_match
def best_matches(self, fallback=None):
"""
Return all the matches in order of quality, with fallback (if
given) at the end.
"""
items = [
i for i, q in sorted(self._parsed, key=lambda iq: -iq[1])]
if fallback:
for index, item in enumerate(items):
if self._match(item, fallback):
items[index+1:] = []
break
else:
items.append(fallback)
return items
def _match(self, item, match):
return item.lower() == match.lower() or item == '*'
class NilAccept(object):
"""
Represents an Accept header with no value.
"""
MasterClass = Accept
def __init__(self, header_name):
self.header_name = header_name
def __repr__(self):
return '<%s for %s: %s>' % (
self.__class__.__name__, self.header_name, self.MasterClass)
def __str__(self):
return ''
def __add__(self, item):
if isinstance(item, self.MasterClass):
return item
else:
return self.MasterClass(self.header_name, '') + item
def __radd__(self, item):
if isinstance(item, self.MasterClass):
return item
else:
return item + self.MasterClass(self.header_name, '')
def __contains__(self, item):
return True
def quality(self, match, default_quality=1):
return 0
def first_match(self, matches):
return matches[0]
def best_match(self, matches, default_match=None):
best_quality = -1
best_match = default_match
for match_item in matches:
if isinstance(match_item, (list, tuple)):
match, quality = match_item
else:
match = match_item
quality = 1
if quality > best_quality:
best_match = match
best_quality = quality
return best_match
def best_matches(self, fallback=None):
if fallback:
return [fallback]
else:
return []
class NoAccept(NilAccept):
def __contains__(self, item):
return False
class MIMEAccept(Accept):
"""
Represents the ``Accept`` header, which is a list of mimetypes.
This class knows about mime wildcards, like ``image/*``
"""
def _match(self, item, match):
item = item.lower()
if item == '*':
item = '*/*'
match = match.lower()
if match == '*':
match = '*/*'
if '/' not in item:
# Bad, but we ignore
return False
if '/' not in match:
raise ValueError(
"MIME matches must include / (bad: %r)" % match)
item_major, item_minor = item.split('/', 1)
match_major, match_minor = match.split('/', 1)
if match_major == '*' and match_minor != '*':
raise ValueError(
"A MIME type of %r doesn't make sense" % match)
if item_major == '*' and item_minor != '*':
# Bad, but we ignore
return False
if ((item_major == '*' and item_minor == '*')
or (match_major == '*' and match_minor == '*')):
return True
if (item_major == match_major
and ((item_minor == '*' or match_minor == '*')
or item_minor == match_minor)):
return True
return False
def accept_html(self):
"""
Returns true if any HTML-like type is accepted
"""
return ('text/html' in self
or 'application/xhtml+xml' in self
or 'application/xml' in self
or 'text/xml' in self)
class MIMENilAccept(NilAccept):
MasterClass = MIMEAccept