forked from flask-api/flask-api
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmediatypes.py
More file actions
112 lines (93 loc) · 3.61 KB
/
Copy pathmediatypes.py
File metadata and controls
112 lines (93 loc) · 3.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# coding: utf8
from __future__ import unicode_literals
class MediaType(object):
def __init__(self, media_type):
self.main_type, self.sub_type, self.params = self._parse(media_type)
@property
def full_type(self):
return self.main_type + '/' + self.sub_type
@property
def precedence(self):
"""
Precedence is determined by how specific a media type is:
3. 'type/subtype; param=val'
2. 'type/subtype'
1. 'type/*'
0. '*/*'
"""
if self.main_type == '*':
return 0
elif self.sub_type == '*':
return 1
elif not self.params or list(self.params.keys()) == ['q']:
return 2
return 3
def satisfies(self, other):
"""
Returns `True` if this media type is a superset of `other`.
Some examples of cases where this holds true:
'application/json; version=1.0' >= 'application/json; version=1.0'
'application/json' >= 'application/json; indent=4'
'text/*' >= 'text/plain'
'*/*' >= 'text/plain'
"""
for key in self.params.keys():
if key != 'q' and other.params.get(key, None) != self.params.get(key, None):
return False
if self.sub_type != '*' and other.sub_type != '*' and other.sub_type != self.sub_type:
return False
if self.main_type != '*' and other.main_type != '*' and other.main_type != self.main_type:
return False
return True
def _parse(self, media_type):
"""
Parse a media type string, like "application/json; indent=4" into a
three-tuple, like: ('application', 'json', {'indent': 4})
"""
full_type, sep, param_string = media_type.partition(';')
params = {}
for token in param_string.strip().split(','):
key, sep, value = [s.strip() for s in token.partition('=')]
if value.startswith('"') and value.endswith('"'):
value = value[1:-1]
if key:
params[key] = value
main_type, sep, sub_type = [s.strip() for s in full_type.partition('/')]
return (main_type, sub_type, params)
def __repr__(self):
return "<%s '%s'>" % (self.__class__.__name__, str(self))
def __str__(self):
"""
Return a canonical string representing the media type.
Note that this ensures the params are sorted.
"""
if self.params:
params_str = ', '.join([
'%s="%s"' % (key, val)
for key, val in sorted(self.params.items())
])
return self.full_type + '; ' + params_str
return self.full_type
def __hash__(self):
return hash(str(self))
def __eq__(self, other):
# Compare two MediaType instances, ignoring parameter ordering.
return (
self.full_type == other.full_type and
self.params == other.params
)
def parse_accept_header(accept):
"""
Parses the value of a clients accept header, and returns a list of sets
of media types it included, ordered by precedence.
For example, 'application/json, application/xml, */*' would return:
[
set([<MediaType "application/xml">, <MediaType "application/json">]),
set([<MediaType "*/*">])
]
"""
ret = [set(), set(), set(), set()]
for token in accept.split(','):
media_type = MediaType(token.strip())
ret[3 - media_type.precedence].add(media_type)
return [media_types for media_types in ret if media_types]