m2m模型翻译
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
7.0 KiB

6 months ago
  1. # SPDX-FileCopyrightText: 2015 Eric Larson
  2. #
  3. # SPDX-License-Identifier: Apache-2.0
  4. from __future__ import annotations
  5. import io
  6. from typing import IO, TYPE_CHECKING, Any, Mapping, cast
  7. from pip._vendor import msgpack
  8. from pip._vendor.requests.structures import CaseInsensitiveDict
  9. from pip._vendor.urllib3 import HTTPResponse
  10. if TYPE_CHECKING:
  11. from pip._vendor.requests import PreparedRequest
  12. class Serializer:
  13. serde_version = "4"
  14. def dumps(
  15. self,
  16. request: PreparedRequest,
  17. response: HTTPResponse,
  18. body: bytes | None = None,
  19. ) -> bytes:
  20. response_headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(
  21. response.headers
  22. )
  23. if body is None:
  24. # When a body isn't passed in, we'll read the response. We
  25. # also update the response with a new file handler to be
  26. # sure it acts as though it was never read.
  27. body = response.read(decode_content=False)
  28. response._fp = io.BytesIO(body) # type: ignore[attr-defined]
  29. response.length_remaining = len(body)
  30. data = {
  31. "response": {
  32. "body": body, # Empty bytestring if body is stored separately
  33. "headers": {str(k): str(v) for k, v in response.headers.items()}, # type: ignore[no-untyped-call]
  34. "status": response.status,
  35. "version": response.version,
  36. "reason": str(response.reason),
  37. "decode_content": response.decode_content,
  38. }
  39. }
  40. # Construct our vary headers
  41. data["vary"] = {}
  42. if "vary" in response_headers:
  43. varied_headers = response_headers["vary"].split(",")
  44. for header in varied_headers:
  45. header = str(header).strip()
  46. header_value = request.headers.get(header, None)
  47. if header_value is not None:
  48. header_value = str(header_value)
  49. data["vary"][header] = header_value
  50. return b",".join([f"cc={self.serde_version}".encode(), self.serialize(data)])
  51. def serialize(self, data: dict[str, Any]) -> bytes:
  52. return cast(bytes, msgpack.dumps(data, use_bin_type=True))
  53. def loads(
  54. self,
  55. request: PreparedRequest,
  56. data: bytes,
  57. body_file: IO[bytes] | None = None,
  58. ) -> HTTPResponse | None:
  59. # Short circuit if we've been given an empty set of data
  60. if not data:
  61. return None
  62. # Determine what version of the serializer the data was serialized
  63. # with
  64. try:
  65. ver, data = data.split(b",", 1)
  66. except ValueError:
  67. ver = b"cc=0"
  68. # Make sure that our "ver" is actually a version and isn't a false
  69. # positive from a , being in the data stream.
  70. if ver[:3] != b"cc=":
  71. data = ver + data
  72. ver = b"cc=0"
  73. # Get the version number out of the cc=N
  74. verstr = ver.split(b"=", 1)[-1].decode("ascii")
  75. # Dispatch to the actual load method for the given version
  76. try:
  77. return getattr(self, f"_loads_v{verstr}")(request, data, body_file) # type: ignore[no-any-return]
  78. except AttributeError:
  79. # This is a version we don't have a loads function for, so we'll
  80. # just treat it as a miss and return None
  81. return None
  82. def prepare_response(
  83. self,
  84. request: PreparedRequest,
  85. cached: Mapping[str, Any],
  86. body_file: IO[bytes] | None = None,
  87. ) -> HTTPResponse | None:
  88. """Verify our vary headers match and construct a real urllib3
  89. HTTPResponse object.
  90. """
  91. # Special case the '*' Vary value as it means we cannot actually
  92. # determine if the cached response is suitable for this request.
  93. # This case is also handled in the controller code when creating
  94. # a cache entry, but is left here for backwards compatibility.
  95. if "*" in cached.get("vary", {}):
  96. return None
  97. # Ensure that the Vary headers for the cached response match our
  98. # request
  99. for header, value in cached.get("vary", {}).items():
  100. if request.headers.get(header, None) != value:
  101. return None
  102. body_raw = cached["response"].pop("body")
  103. headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(
  104. data=cached["response"]["headers"]
  105. )
  106. if headers.get("transfer-encoding", "") == "chunked":
  107. headers.pop("transfer-encoding")
  108. cached["response"]["headers"] = headers
  109. try:
  110. body: IO[bytes]
  111. if body_file is None:
  112. body = io.BytesIO(body_raw)
  113. else:
  114. body = body_file
  115. except TypeError:
  116. # This can happen if cachecontrol serialized to v1 format (pickle)
  117. # using Python 2. A Python 2 str(byte string) will be unpickled as
  118. # a Python 3 str (unicode string), which will cause the above to
  119. # fail with:
  120. #
  121. # TypeError: 'str' does not support the buffer interface
  122. body = io.BytesIO(body_raw.encode("utf8"))
  123. # Discard any `strict` parameter serialized by older version of cachecontrol.
  124. cached["response"].pop("strict", None)
  125. return HTTPResponse(body=body, preload_content=False, **cached["response"])
  126. def _loads_v0(
  127. self,
  128. request: PreparedRequest,
  129. data: bytes,
  130. body_file: IO[bytes] | None = None,
  131. ) -> None:
  132. # The original legacy cache data. This doesn't contain enough
  133. # information to construct everything we need, so we'll treat this as
  134. # a miss.
  135. return None
  136. def _loads_v1(
  137. self,
  138. request: PreparedRequest,
  139. data: bytes,
  140. body_file: IO[bytes] | None = None,
  141. ) -> HTTPResponse | None:
  142. # The "v1" pickled cache format. This is no longer supported
  143. # for security reasons, so we treat it as a miss.
  144. return None
  145. def _loads_v2(
  146. self,
  147. request: PreparedRequest,
  148. data: bytes,
  149. body_file: IO[bytes] | None = None,
  150. ) -> HTTPResponse | None:
  151. # The "v2" compressed base64 cache format.
  152. # This has been removed due to age and poor size/performance
  153. # characteristics, so we treat it as a miss.
  154. return None
  155. def _loads_v3(
  156. self,
  157. request: PreparedRequest,
  158. data: bytes,
  159. body_file: IO[bytes] | None = None,
  160. ) -> None:
  161. # Due to Python 2 encoding issues, it's impossible to know for sure
  162. # exactly how to load v3 entries, thus we'll treat these as a miss so
  163. # that they get rewritten out as v4 entries.
  164. return None
  165. def _loads_v4(
  166. self,
  167. request: PreparedRequest,
  168. data: bytes,
  169. body_file: IO[bytes] | None = None,
  170. ) -> HTTPResponse | None:
  171. try:
  172. cached = msgpack.loads(data, raw=False)
  173. except ValueError:
  174. return None
  175. return self.prepare_response(request, cached, body_file)