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