Загрузить файлы в «venv/Lib/site-packages/pip/_vendor/cachecontrol»
This commit is contained in:
13
venv/Lib/site-packages/pip/_vendor/cachecontrol/LICENSE.txt
Normal file
13
venv/Lib/site-packages/pip/_vendor/cachecontrol/LICENSE.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Copyright 2012-2021 Eric Larson
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
121
venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py
Normal file
121
venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2015 Eric Larson
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import mmap
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
from typing import TYPE_CHECKING, Any, Callable
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Buffer
|
||||||
|
from http.client import HTTPResponse
|
||||||
|
|
||||||
|
|
||||||
|
class CallbackFileWrapper:
|
||||||
|
"""
|
||||||
|
Small wrapper around a fp object which will tee everything read into a
|
||||||
|
buffer, and when that file is closed it will execute a callback with the
|
||||||
|
contents of that buffer.
|
||||||
|
|
||||||
|
All attributes are proxied to the underlying file object.
|
||||||
|
|
||||||
|
This class uses members with a double underscore (__) leading prefix so as
|
||||||
|
not to accidentally shadow an attribute.
|
||||||
|
|
||||||
|
The data is stored in a temporary file until it is all available. As long
|
||||||
|
as the temporary files directory is disk-based (sometimes it's a
|
||||||
|
memory-backed-``tmpfs`` on Linux), data will be unloaded to disk if memory
|
||||||
|
pressure is high. For small files the disk usually won't be used at all,
|
||||||
|
it'll all be in the filesystem memory cache, so there should be no
|
||||||
|
performance impact.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, fp: HTTPResponse, callback: Callable[[Buffer], None] | None
|
||||||
|
) -> None:
|
||||||
|
self.__buf = NamedTemporaryFile("rb+", delete=True)
|
||||||
|
self.__fp = fp
|
||||||
|
self.__callback = callback
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> Any:
|
||||||
|
# The vagaries of garbage collection means that self.__fp is
|
||||||
|
# not always set. By using __getattribute__ and the private
|
||||||
|
# name[0] allows looking up the attribute value and raising an
|
||||||
|
# AttributeError when it doesn't exist. This stop things from
|
||||||
|
# infinitely recursing calls to getattr in the case where
|
||||||
|
# self.__fp hasn't been set.
|
||||||
|
#
|
||||||
|
# [0] https://docs.python.org/2/reference/expressions.html#atom-identifiers
|
||||||
|
fp = self.__getattribute__("_CallbackFileWrapper__fp")
|
||||||
|
return getattr(fp, name)
|
||||||
|
|
||||||
|
def __is_fp_closed(self) -> bool:
|
||||||
|
try:
|
||||||
|
return self.__fp.fp is None
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
closed: bool = self.__fp.closed
|
||||||
|
return closed
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# We just don't cache it then.
|
||||||
|
# TODO: Add some logging here...
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _close(self) -> None:
|
||||||
|
result: Buffer
|
||||||
|
if self.__callback:
|
||||||
|
if self.__buf.tell() == 0:
|
||||||
|
# Empty file:
|
||||||
|
result = b""
|
||||||
|
else:
|
||||||
|
# Return the data without actually loading it into memory,
|
||||||
|
# relying on Python's buffer API and mmap(). mmap() just gives
|
||||||
|
# a view directly into the filesystem's memory cache, so it
|
||||||
|
# doesn't result in duplicate memory use.
|
||||||
|
self.__buf.seek(0, 0)
|
||||||
|
result = memoryview(
|
||||||
|
mmap.mmap(self.__buf.fileno(), 0, access=mmap.ACCESS_READ)
|
||||||
|
)
|
||||||
|
self.__callback(result)
|
||||||
|
|
||||||
|
# We assign this to None here, because otherwise we can get into
|
||||||
|
# really tricky problems where the CPython interpreter dead locks
|
||||||
|
# because the callback is holding a reference to something which
|
||||||
|
# has a __del__ method. Setting this to None breaks the cycle
|
||||||
|
# and allows the garbage collector to do it's thing normally.
|
||||||
|
self.__callback = None
|
||||||
|
|
||||||
|
# Closing the temporary file releases memory and frees disk space.
|
||||||
|
# Important when caching big files.
|
||||||
|
self.__buf.close()
|
||||||
|
|
||||||
|
def read(self, amt: int | None = None) -> bytes:
|
||||||
|
data: bytes = self.__fp.read(amt)
|
||||||
|
if data:
|
||||||
|
# We may be dealing with b'', a sign that things are over:
|
||||||
|
# it's passed e.g. after we've already closed self.__buf.
|
||||||
|
self.__buf.write(data)
|
||||||
|
if self.__is_fp_closed():
|
||||||
|
self._close()
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def _safe_read(self, amt: int) -> bytes:
|
||||||
|
data: bytes = self.__fp._safe_read(amt) # type: ignore[attr-defined]
|
||||||
|
if amt == 2 and data == b"\r\n":
|
||||||
|
# urllib executes this read to toss the CRLF at the end
|
||||||
|
# of the chunk.
|
||||||
|
return data
|
||||||
|
|
||||||
|
self.__buf.write(data)
|
||||||
|
if self.__is_fp_closed():
|
||||||
|
self._close()
|
||||||
|
|
||||||
|
return data
|
||||||
157
venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py
Normal file
157
venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2015 Eric Larson
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import calendar
|
||||||
|
import time
|
||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from email.utils import formatdate, parsedate, parsedate_tz
|
||||||
|
from typing import TYPE_CHECKING, Any, Mapping
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pip._vendor.urllib3 import HTTPResponse
|
||||||
|
|
||||||
|
TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT"
|
||||||
|
|
||||||
|
|
||||||
|
def expire_after(delta: timedelta, date: datetime | None = None) -> datetime:
|
||||||
|
date = date or datetime.now(timezone.utc)
|
||||||
|
return date + delta
|
||||||
|
|
||||||
|
|
||||||
|
def datetime_to_header(dt: datetime) -> str:
|
||||||
|
return formatdate(calendar.timegm(dt.timetuple()))
|
||||||
|
|
||||||
|
|
||||||
|
class BaseHeuristic:
|
||||||
|
def warning(self, response: HTTPResponse) -> str | None:
|
||||||
|
"""
|
||||||
|
Return a valid 1xx warning header value describing the cache
|
||||||
|
adjustments.
|
||||||
|
|
||||||
|
The response is provided too allow warnings like 113
|
||||||
|
http://tools.ietf.org/html/rfc7234#section-5.5.4 where we need
|
||||||
|
to explicitly say response is over 24 hours old.
|
||||||
|
"""
|
||||||
|
return '110 - "Response is Stale"'
|
||||||
|
|
||||||
|
def update_headers(self, response: HTTPResponse) -> dict[str, str]:
|
||||||
|
"""Update the response headers with any new headers.
|
||||||
|
|
||||||
|
NOTE: This SHOULD always include some Warning header to
|
||||||
|
signify that the response was cached by the client, not
|
||||||
|
by way of the provided headers.
|
||||||
|
"""
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def apply(self, response: HTTPResponse) -> HTTPResponse:
|
||||||
|
updated_headers = self.update_headers(response)
|
||||||
|
|
||||||
|
if updated_headers:
|
||||||
|
response.headers.update(updated_headers)
|
||||||
|
warning_header_value = self.warning(response)
|
||||||
|
if warning_header_value is not None:
|
||||||
|
response.headers.update({"Warning": warning_header_value})
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class OneDayCache(BaseHeuristic):
|
||||||
|
"""
|
||||||
|
Cache the response by providing an expires 1 day in the
|
||||||
|
future.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def update_headers(self, response: HTTPResponse) -> dict[str, str]:
|
||||||
|
headers = {}
|
||||||
|
|
||||||
|
if "expires" not in response.headers:
|
||||||
|
date = parsedate(response.headers["date"])
|
||||||
|
expires = expire_after(
|
||||||
|
timedelta(days=1),
|
||||||
|
date=datetime(*date[:6], tzinfo=timezone.utc), # type: ignore[index,misc]
|
||||||
|
)
|
||||||
|
headers["expires"] = datetime_to_header(expires)
|
||||||
|
headers["cache-control"] = "public"
|
||||||
|
return headers
|
||||||
|
|
||||||
|
|
||||||
|
class ExpiresAfter(BaseHeuristic):
|
||||||
|
"""
|
||||||
|
Cache **all** requests for a defined time period.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kw: Any) -> None:
|
||||||
|
self.delta = timedelta(**kw)
|
||||||
|
|
||||||
|
def update_headers(self, response: HTTPResponse) -> dict[str, str]:
|
||||||
|
expires = expire_after(self.delta)
|
||||||
|
return {"expires": datetime_to_header(expires), "cache-control": "public"}
|
||||||
|
|
||||||
|
def warning(self, response: HTTPResponse) -> str | None:
|
||||||
|
tmpl = "110 - Automatically cached for %s. Response might be stale"
|
||||||
|
return tmpl % self.delta
|
||||||
|
|
||||||
|
|
||||||
|
class LastModified(BaseHeuristic):
|
||||||
|
"""
|
||||||
|
If there is no Expires header already, fall back on Last-Modified
|
||||||
|
using the heuristic from
|
||||||
|
http://tools.ietf.org/html/rfc7234#section-4.2.2
|
||||||
|
to calculate a reasonable value.
|
||||||
|
|
||||||
|
Firefox also does something like this per
|
||||||
|
https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ
|
||||||
|
http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397
|
||||||
|
Unlike mozilla we limit this to 24-hr.
|
||||||
|
"""
|
||||||
|
|
||||||
|
cacheable_by_default_statuses = {
|
||||||
|
200,
|
||||||
|
203,
|
||||||
|
204,
|
||||||
|
206,
|
||||||
|
300,
|
||||||
|
301,
|
||||||
|
404,
|
||||||
|
405,
|
||||||
|
410,
|
||||||
|
414,
|
||||||
|
501,
|
||||||
|
}
|
||||||
|
|
||||||
|
def update_headers(self, resp: HTTPResponse) -> dict[str, str]:
|
||||||
|
headers: Mapping[str, str] = resp.headers
|
||||||
|
|
||||||
|
if "expires" in headers:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if "cache-control" in headers and headers["cache-control"] != "public":
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if resp.status not in self.cacheable_by_default_statuses:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if "date" not in headers or "last-modified" not in headers:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
time_tuple = parsedate_tz(headers["date"])
|
||||||
|
assert time_tuple is not None
|
||||||
|
date = calendar.timegm(time_tuple[:6])
|
||||||
|
last_modified = parsedate(headers["last-modified"])
|
||||||
|
if last_modified is None:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
current_age = max(0, now - date)
|
||||||
|
delta = date - calendar.timegm(last_modified)
|
||||||
|
freshness_lifetime = max(0, min(delta / 10, 24 * 3600))
|
||||||
|
if freshness_lifetime <= current_age:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
expires = date + freshness_lifetime
|
||||||
|
return {"expires": time.strftime(TIME_FMT, time.gmtime(expires))}
|
||||||
|
|
||||||
|
def warning(self, resp: HTTPResponse) -> str | None:
|
||||||
|
return None
|
||||||
BIN
venv/Lib/site-packages/pip/_vendor/cachecontrol/py.typed
Normal file
BIN
venv/Lib/site-packages/pip/_vendor/cachecontrol/py.typed
Normal file
Binary file not shown.
146
venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py
Normal file
146
venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2015 Eric Larson
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import io
|
||||||
|
from typing import IO, TYPE_CHECKING, Any, Mapping, cast
|
||||||
|
|
||||||
|
from pip._vendor import msgpack
|
||||||
|
from pip._vendor.requests.structures import CaseInsensitiveDict
|
||||||
|
from pip._vendor.urllib3 import HTTPResponse
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pip._vendor.requests import PreparedRequest
|
||||||
|
|
||||||
|
|
||||||
|
class Serializer:
|
||||||
|
serde_version = "4"
|
||||||
|
|
||||||
|
def dumps(
|
||||||
|
self,
|
||||||
|
request: PreparedRequest,
|
||||||
|
response: HTTPResponse,
|
||||||
|
body: bytes | None = None,
|
||||||
|
) -> bytes:
|
||||||
|
response_headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(
|
||||||
|
response.headers
|
||||||
|
)
|
||||||
|
|
||||||
|
if body is None:
|
||||||
|
# When a body isn't passed in, we'll read the response. We
|
||||||
|
# also update the response with a new file handler to be
|
||||||
|
# sure it acts as though it was never read.
|
||||||
|
body = response.read(decode_content=False)
|
||||||
|
response._fp = io.BytesIO(body) # type: ignore[assignment]
|
||||||
|
response.length_remaining = len(body)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"response": {
|
||||||
|
"body": body, # Empty bytestring if body is stored separately
|
||||||
|
"headers": {str(k): str(v) for k, v in response.headers.items()},
|
||||||
|
"status": response.status,
|
||||||
|
"version": response.version,
|
||||||
|
"reason": str(response.reason),
|
||||||
|
"decode_content": response.decode_content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Construct our vary headers
|
||||||
|
data["vary"] = {}
|
||||||
|
if "vary" in response_headers:
|
||||||
|
varied_headers = response_headers["vary"].split(",")
|
||||||
|
for header in varied_headers:
|
||||||
|
header = str(header).strip()
|
||||||
|
header_value = request.headers.get(header, None)
|
||||||
|
if header_value is not None:
|
||||||
|
header_value = str(header_value)
|
||||||
|
data["vary"][header] = header_value
|
||||||
|
|
||||||
|
return b",".join([f"cc={self.serde_version}".encode(), self.serialize(data)])
|
||||||
|
|
||||||
|
def serialize(self, data: dict[str, Any]) -> bytes:
|
||||||
|
return cast(bytes, msgpack.dumps(data, use_bin_type=True))
|
||||||
|
|
||||||
|
def loads(
|
||||||
|
self,
|
||||||
|
request: PreparedRequest,
|
||||||
|
data: bytes,
|
||||||
|
body_file: IO[bytes] | None = None,
|
||||||
|
) -> HTTPResponse | None:
|
||||||
|
# Short circuit if we've been given an empty set of data
|
||||||
|
if not data:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Previous versions of this library supported other serialization
|
||||||
|
# formats, but these have all been removed.
|
||||||
|
if not data.startswith(f"cc={self.serde_version},".encode()):
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = data[5:]
|
||||||
|
return self._loads_v4(request, data, body_file)
|
||||||
|
|
||||||
|
def prepare_response(
|
||||||
|
self,
|
||||||
|
request: PreparedRequest,
|
||||||
|
cached: Mapping[str, Any],
|
||||||
|
body_file: IO[bytes] | None = None,
|
||||||
|
) -> HTTPResponse | None:
|
||||||
|
"""Verify our vary headers match and construct a real urllib3
|
||||||
|
HTTPResponse object.
|
||||||
|
"""
|
||||||
|
# Special case the '*' Vary value as it means we cannot actually
|
||||||
|
# determine if the cached response is suitable for this request.
|
||||||
|
# This case is also handled in the controller code when creating
|
||||||
|
# a cache entry, but is left here for backwards compatibility.
|
||||||
|
if "*" in cached.get("vary", {}):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Ensure that the Vary headers for the cached response match our
|
||||||
|
# request
|
||||||
|
for header, value in cached.get("vary", {}).items():
|
||||||
|
if request.headers.get(header, None) != value:
|
||||||
|
return None
|
||||||
|
|
||||||
|
body_raw = cached["response"].pop("body")
|
||||||
|
|
||||||
|
headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(
|
||||||
|
data=cached["response"]["headers"]
|
||||||
|
)
|
||||||
|
if headers.get("transfer-encoding", "") == "chunked":
|
||||||
|
headers.pop("transfer-encoding")
|
||||||
|
|
||||||
|
cached["response"]["headers"] = headers
|
||||||
|
|
||||||
|
try:
|
||||||
|
body: IO[bytes]
|
||||||
|
if body_file is None:
|
||||||
|
body = io.BytesIO(body_raw)
|
||||||
|
else:
|
||||||
|
body = body_file
|
||||||
|
except TypeError:
|
||||||
|
# This can happen if cachecontrol serialized to v1 format (pickle)
|
||||||
|
# using Python 2. A Python 2 str(byte string) will be unpickled as
|
||||||
|
# a Python 3 str (unicode string), which will cause the above to
|
||||||
|
# fail with:
|
||||||
|
#
|
||||||
|
# TypeError: 'str' does not support the buffer interface
|
||||||
|
body = io.BytesIO(body_raw.encode("utf8"))
|
||||||
|
|
||||||
|
# Discard any `strict` parameter serialized by older version of cachecontrol.
|
||||||
|
cached["response"].pop("strict", None)
|
||||||
|
|
||||||
|
return HTTPResponse(body=body, preload_content=False, **cached["response"])
|
||||||
|
|
||||||
|
def _loads_v4(
|
||||||
|
self,
|
||||||
|
request: PreparedRequest,
|
||||||
|
data: bytes,
|
||||||
|
body_file: IO[bytes] | None = None,
|
||||||
|
) -> HTTPResponse | None:
|
||||||
|
try:
|
||||||
|
cached = msgpack.loads(data, raw=False)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.prepare_response(request, cached, body_file)
|
||||||
Reference in New Issue
Block a user