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) |
