Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/cachecontrol/serialize.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
1 import base64 | |
2 import io | |
3 import json | |
4 import zlib | |
5 | |
6 from requests.structures import CaseInsensitiveDict | |
7 | |
8 from .compat import HTTPResponse, pickle, text_type | |
9 | |
10 | |
11 def _b64_encode_bytes(b): | |
12 return base64.b64encode(b).decode("ascii") | |
13 | |
14 | |
15 def _b64_encode_str(s): | |
16 return _b64_encode_bytes(s.encode("utf8")) | |
17 | |
18 | |
19 def _b64_encode(s): | |
20 if isinstance(s, text_type): | |
21 return _b64_encode_str(s) | |
22 return _b64_encode_bytes(s) | |
23 | |
24 | |
25 def _b64_decode_bytes(b): | |
26 return base64.b64decode(b.encode("ascii")) | |
27 | |
28 | |
29 def _b64_decode_str(s): | |
30 return _b64_decode_bytes(s).decode("utf8") | |
31 | |
32 | |
33 class Serializer(object): | |
34 | |
35 def dumps(self, request, response, body=None): | |
36 response_headers = CaseInsensitiveDict(response.headers) | |
37 | |
38 if body is None: | |
39 body = response.read(decode_content=False) | |
40 | |
41 # NOTE: 99% sure this is dead code. I'm only leaving it | |
42 # here b/c I don't have a test yet to prove | |
43 # it. Basically, before using | |
44 # `cachecontrol.filewrapper.CallbackFileWrapper`, | |
45 # this made an effort to reset the file handle. The | |
46 # `CallbackFileWrapper` short circuits this code by | |
47 # setting the body as the content is consumed, the | |
48 # result being a `body` argument is *always* passed | |
49 # into cache_response, and in turn, | |
50 # `Serializer.dump`. | |
51 response._fp = io.BytesIO(body) | |
52 | |
53 data = { | |
54 "response": { | |
55 "body": _b64_encode_bytes(body), | |
56 "headers": dict( | |
57 (_b64_encode(k), _b64_encode(v)) | |
58 for k, v in response.headers.items() | |
59 ), | |
60 "status": response.status, | |
61 "version": response.version, | |
62 "reason": _b64_encode_str(response.reason), | |
63 "strict": response.strict, | |
64 "decode_content": response.decode_content, | |
65 }, | |
66 } | |
67 | |
68 # Construct our vary headers | |
69 data["vary"] = {} | |
70 if "vary" in response_headers: | |
71 varied_headers = response_headers['vary'].split(',') | |
72 for header in varied_headers: | |
73 header = header.strip() | |
74 data["vary"][header] = request.headers.get(header, None) | |
75 | |
76 # Encode our Vary headers to ensure they can be serialized as JSON | |
77 data["vary"] = dict( | |
78 (_b64_encode(k), _b64_encode(v) if v is not None else v) | |
79 for k, v in data["vary"].items() | |
80 ) | |
81 | |
82 return b",".join([ | |
83 b"cc=2", | |
84 zlib.compress( | |
85 json.dumps( | |
86 data, separators=(",", ":"), sort_keys=True, | |
87 ).encode("utf8"), | |
88 ), | |
89 ]) | |
90 | |
91 def loads(self, request, data): | |
92 # Short circuit if we've been given an empty set of data | |
93 if not data: | |
94 return | |
95 | |
96 # Determine what version of the serializer the data was serialized | |
97 # with | |
98 try: | |
99 ver, data = data.split(b",", 1) | |
100 except ValueError: | |
101 ver = b"cc=0" | |
102 | |
103 # Make sure that our "ver" is actually a version and isn't a false | |
104 # positive from a , being in the data stream. | |
105 if ver[:3] != b"cc=": | |
106 data = ver + data | |
107 ver = b"cc=0" | |
108 | |
109 # Get the version number out of the cc=N | |
110 ver = ver.split(b"=", 1)[-1].decode("ascii") | |
111 | |
112 # Dispatch to the actual load method for the given version | |
113 try: | |
114 return getattr(self, "_loads_v{0}".format(ver))(request, data) | |
115 except AttributeError: | |
116 # This is a version we don't have a loads function for, so we'll | |
117 # just treat it as a miss and return None | |
118 return | |
119 | |
120 def prepare_response(self, request, cached): | |
121 """Verify our vary headers match and construct a real urllib3 | |
122 HTTPResponse object. | |
123 """ | |
124 # Special case the '*' Vary value as it means we cannot actually | |
125 # determine if the cached response is suitable for this request. | |
126 if "*" in cached.get("vary", {}): | |
127 return | |
128 | |
129 # Ensure that the Vary headers for the cached response match our | |
130 # request | |
131 for header, value in cached.get("vary", {}).items(): | |
132 if request.headers.get(header, None) != value: | |
133 return | |
134 | |
135 body_raw = cached["response"].pop("body") | |
136 | |
137 headers = CaseInsensitiveDict(data=cached['response']['headers']) | |
138 if headers.get('transfer-encoding', '') == 'chunked': | |
139 headers.pop('transfer-encoding') | |
140 | |
141 cached['response']['headers'] = headers | |
142 | |
143 try: | |
144 body = io.BytesIO(body_raw) | |
145 except TypeError: | |
146 # This can happen if cachecontrol serialized to v1 format (pickle) | |
147 # using Python 2. A Python 2 str(byte string) will be unpickled as | |
148 # a Python 3 str (unicode string), which will cause the above to | |
149 # fail with: | |
150 # | |
151 # TypeError: 'str' does not support the buffer interface | |
152 body = io.BytesIO(body_raw.encode('utf8')) | |
153 | |
154 return HTTPResponse( | |
155 body=body, | |
156 preload_content=False, | |
157 **cached["response"] | |
158 ) | |
159 | |
160 def _loads_v0(self, request, data): | |
161 # The original legacy cache data. This doesn't contain enough | |
162 # information to construct everything we need, so we'll treat this as | |
163 # a miss. | |
164 return | |
165 | |
166 def _loads_v1(self, request, data): | |
167 try: | |
168 cached = pickle.loads(data) | |
169 except ValueError: | |
170 return | |
171 | |
172 return self.prepare_response(request, cached) | |
173 | |
174 def _loads_v2(self, request, data): | |
175 try: | |
176 cached = json.loads(zlib.decompress(data).decode("utf8")) | |
177 except ValueError: | |
178 return | |
179 | |
180 # We need to decode the items that we've base64 encoded | |
181 cached["response"]["body"] = _b64_decode_bytes( | |
182 cached["response"]["body"] | |
183 ) | |
184 cached["response"]["headers"] = dict( | |
185 (_b64_decode_str(k), _b64_decode_str(v)) | |
186 for k, v in cached["response"]["headers"].items() | |
187 ) | |
188 cached["response"]["reason"] = _b64_decode_str( | |
189 cached["response"]["reason"], | |
190 ) | |
191 cached["vary"] = dict( | |
192 (_b64_decode_str(k), _b64_decode_str(v) if v is not None else v) | |
193 for k, v in cached["vary"].items() | |
194 ) | |
195 | |
196 return self.prepare_response(request, cached) |