mirror of
https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git
synced 2025-08-14 00:25:46 +02:00
Все подряд
This commit is contained in:
516
.venv2/Lib/site-packages/httpcore/_models.py
Normal file
516
.venv2/Lib/site-packages/httpcore/_models.py
Normal file
@@ -0,0 +1,516 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import ssl
|
||||
import typing
|
||||
import urllib.parse
|
||||
|
||||
# Functions for typechecking...
|
||||
|
||||
|
||||
ByteOrStr = typing.Union[bytes, str]
|
||||
HeadersAsSequence = typing.Sequence[typing.Tuple[ByteOrStr, ByteOrStr]]
|
||||
HeadersAsMapping = typing.Mapping[ByteOrStr, ByteOrStr]
|
||||
HeaderTypes = typing.Union[HeadersAsSequence, HeadersAsMapping, None]
|
||||
|
||||
Extensions = typing.MutableMapping[str, typing.Any]
|
||||
|
||||
|
||||
def enforce_bytes(value: bytes | str, *, name: str) -> bytes:
|
||||
"""
|
||||
Any arguments that are ultimately represented as bytes can be specified
|
||||
either as bytes or as strings.
|
||||
|
||||
However we enforce that any string arguments must only contain characters in
|
||||
the plain ASCII range. chr(0)...chr(127). If you need to use characters
|
||||
outside that range then be precise, and use a byte-wise argument.
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return value.encode("ascii")
|
||||
except UnicodeEncodeError:
|
||||
raise TypeError(f"{name} strings may not include unicode characters.")
|
||||
elif isinstance(value, bytes):
|
||||
return value
|
||||
|
||||
seen_type = type(value).__name__
|
||||
raise TypeError(f"{name} must be bytes or str, but got {seen_type}.")
|
||||
|
||||
|
||||
def enforce_url(value: URL | bytes | str, *, name: str) -> URL:
|
||||
"""
|
||||
Type check for URL parameters.
|
||||
"""
|
||||
if isinstance(value, (bytes, str)):
|
||||
return URL(value)
|
||||
elif isinstance(value, URL):
|
||||
return value
|
||||
|
||||
seen_type = type(value).__name__
|
||||
raise TypeError(f"{name} must be a URL, bytes, or str, but got {seen_type}.")
|
||||
|
||||
|
||||
def enforce_headers(
|
||||
value: HeadersAsMapping | HeadersAsSequence | None = None, *, name: str
|
||||
) -> list[tuple[bytes, bytes]]:
|
||||
"""
|
||||
Convienence function that ensure all items in request or response headers
|
||||
are either bytes or strings in the plain ASCII range.
|
||||
"""
|
||||
if value is None:
|
||||
return []
|
||||
elif isinstance(value, typing.Mapping):
|
||||
return [
|
||||
(
|
||||
enforce_bytes(k, name="header name"),
|
||||
enforce_bytes(v, name="header value"),
|
||||
)
|
||||
for k, v in value.items()
|
||||
]
|
||||
elif isinstance(value, typing.Sequence):
|
||||
return [
|
||||
(
|
||||
enforce_bytes(k, name="header name"),
|
||||
enforce_bytes(v, name="header value"),
|
||||
)
|
||||
for k, v in value
|
||||
]
|
||||
|
||||
seen_type = type(value).__name__
|
||||
raise TypeError(
|
||||
f"{name} must be a mapping or sequence of two-tuples, but got {seen_type}."
|
||||
)
|
||||
|
||||
|
||||
def enforce_stream(
|
||||
value: bytes | typing.Iterable[bytes] | typing.AsyncIterable[bytes] | None,
|
||||
*,
|
||||
name: str,
|
||||
) -> typing.Iterable[bytes] | typing.AsyncIterable[bytes]:
|
||||
if value is None:
|
||||
return ByteStream(b"")
|
||||
elif isinstance(value, bytes):
|
||||
return ByteStream(value)
|
||||
return value
|
||||
|
||||
|
||||
# * https://tools.ietf.org/html/rfc3986#section-3.2.3
|
||||
# * https://url.spec.whatwg.org/#url-miscellaneous
|
||||
# * https://url.spec.whatwg.org/#scheme-state
|
||||
DEFAULT_PORTS = {
|
||||
b"ftp": 21,
|
||||
b"http": 80,
|
||||
b"https": 443,
|
||||
b"ws": 80,
|
||||
b"wss": 443,
|
||||
}
|
||||
|
||||
|
||||
def include_request_headers(
|
||||
headers: list[tuple[bytes, bytes]],
|
||||
*,
|
||||
url: "URL",
|
||||
content: None | bytes | typing.Iterable[bytes] | typing.AsyncIterable[bytes],
|
||||
) -> list[tuple[bytes, bytes]]:
|
||||
headers_set = set(k.lower() for k, v in headers)
|
||||
|
||||
if b"host" not in headers_set:
|
||||
default_port = DEFAULT_PORTS.get(url.scheme)
|
||||
if url.port is None or url.port == default_port:
|
||||
header_value = url.host
|
||||
else:
|
||||
header_value = b"%b:%d" % (url.host, url.port)
|
||||
headers = [(b"Host", header_value)] + headers
|
||||
|
||||
if (
|
||||
content is not None
|
||||
and b"content-length" not in headers_set
|
||||
and b"transfer-encoding" not in headers_set
|
||||
):
|
||||
if isinstance(content, bytes):
|
||||
content_length = str(len(content)).encode("ascii")
|
||||
headers += [(b"Content-Length", content_length)]
|
||||
else:
|
||||
headers += [(b"Transfer-Encoding", b"chunked")] # pragma: nocover
|
||||
|
||||
return headers
|
||||
|
||||
|
||||
# Interfaces for byte streams...
|
||||
|
||||
|
||||
class ByteStream:
|
||||
"""
|
||||
A container for non-streaming content, and that supports both sync and async
|
||||
stream iteration.
|
||||
"""
|
||||
|
||||
def __init__(self, content: bytes) -> None:
|
||||
self._content = content
|
||||
|
||||
def __iter__(self) -> typing.Iterator[bytes]:
|
||||
yield self._content
|
||||
|
||||
async def __aiter__(self) -> typing.AsyncIterator[bytes]:
|
||||
yield self._content
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__.__name__} [{len(self._content)} bytes]>"
|
||||
|
||||
|
||||
class Origin:
|
||||
def __init__(self, scheme: bytes, host: bytes, port: int) -> None:
|
||||
self.scheme = scheme
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
def __eq__(self, other: typing.Any) -> bool:
|
||||
return (
|
||||
isinstance(other, Origin)
|
||||
and self.scheme == other.scheme
|
||||
and self.host == other.host
|
||||
and self.port == other.port
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
scheme = self.scheme.decode("ascii")
|
||||
host = self.host.decode("ascii")
|
||||
port = str(self.port)
|
||||
return f"{scheme}://{host}:{port}"
|
||||
|
||||
|
||||
class URL:
|
||||
"""
|
||||
Represents the URL against which an HTTP request may be made.
|
||||
|
||||
The URL may either be specified as a plain string, for convienence:
|
||||
|
||||
```python
|
||||
url = httpcore.URL("https://www.example.com/")
|
||||
```
|
||||
|
||||
Or be constructed with explicitily pre-parsed components:
|
||||
|
||||
```python
|
||||
url = httpcore.URL(scheme=b'https', host=b'www.example.com', port=None, target=b'/')
|
||||
```
|
||||
|
||||
Using this second more explicit style allows integrations that are using
|
||||
`httpcore` to pass through URLs that have already been parsed in order to use
|
||||
libraries such as `rfc-3986` rather than relying on the stdlib. It also ensures
|
||||
that URL parsing is treated identically at both the networking level and at any
|
||||
higher layers of abstraction.
|
||||
|
||||
The four components are important here, as they allow the URL to be precisely
|
||||
specified in a pre-parsed format. They also allow certain types of request to
|
||||
be created that could not otherwise be expressed.
|
||||
|
||||
For example, an HTTP request to `http://www.example.com/` forwarded via a proxy
|
||||
at `http://localhost:8080`...
|
||||
|
||||
```python
|
||||
# Constructs an HTTP request with a complete URL as the target:
|
||||
# GET https://www.example.com/ HTTP/1.1
|
||||
url = httpcore.URL(
|
||||
scheme=b'http',
|
||||
host=b'localhost',
|
||||
port=8080,
|
||||
target=b'https://www.example.com/'
|
||||
)
|
||||
request = httpcore.Request(
|
||||
method="GET",
|
||||
url=url
|
||||
)
|
||||
```
|
||||
|
||||
Another example is constructing an `OPTIONS *` request...
|
||||
|
||||
```python
|
||||
# Constructs an 'OPTIONS *' HTTP request:
|
||||
# OPTIONS * HTTP/1.1
|
||||
url = httpcore.URL(scheme=b'https', host=b'www.example.com', target=b'*')
|
||||
request = httpcore.Request(method="OPTIONS", url=url)
|
||||
```
|
||||
|
||||
This kind of request is not possible to formulate with a URL string,
|
||||
because the `/` delimiter is always used to demark the target from the
|
||||
host/port portion of the URL.
|
||||
|
||||
For convenience, string-like arguments may be specified either as strings or
|
||||
as bytes. However, once a request is being issue over-the-wire, the URL
|
||||
components are always ultimately required to be a bytewise representation.
|
||||
|
||||
In order to avoid any ambiguity over character encodings, when strings are used
|
||||
as arguments, they must be strictly limited to the ASCII range `chr(0)`-`chr(127)`.
|
||||
If you require a bytewise representation that is outside this range you must
|
||||
handle the character encoding directly, and pass a bytes instance.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
url: bytes | str = "",
|
||||
*,
|
||||
scheme: bytes | str = b"",
|
||||
host: bytes | str = b"",
|
||||
port: int | None = None,
|
||||
target: bytes | str = b"",
|
||||
) -> None:
|
||||
"""
|
||||
Parameters:
|
||||
url: The complete URL as a string or bytes.
|
||||
scheme: The URL scheme as a string or bytes.
|
||||
Typically either `"http"` or `"https"`.
|
||||
host: The URL host as a string or bytes. Such as `"www.example.com"`.
|
||||
port: The port to connect to. Either an integer or `None`.
|
||||
target: The target of the HTTP request. Such as `"/items?search=red"`.
|
||||
"""
|
||||
if url:
|
||||
parsed = urllib.parse.urlparse(enforce_bytes(url, name="url"))
|
||||
self.scheme = parsed.scheme
|
||||
self.host = parsed.hostname or b""
|
||||
self.port = parsed.port
|
||||
self.target = (parsed.path or b"/") + (
|
||||
b"?" + parsed.query if parsed.query else b""
|
||||
)
|
||||
else:
|
||||
self.scheme = enforce_bytes(scheme, name="scheme")
|
||||
self.host = enforce_bytes(host, name="host")
|
||||
self.port = port
|
||||
self.target = enforce_bytes(target, name="target")
|
||||
|
||||
@property
|
||||
def origin(self) -> Origin:
|
||||
default_port = {
|
||||
b"http": 80,
|
||||
b"https": 443,
|
||||
b"ws": 80,
|
||||
b"wss": 443,
|
||||
b"socks5": 1080,
|
||||
b"socks5h": 1080,
|
||||
}[self.scheme]
|
||||
return Origin(
|
||||
scheme=self.scheme, host=self.host, port=self.port or default_port
|
||||
)
|
||||
|
||||
def __eq__(self, other: typing.Any) -> bool:
|
||||
return (
|
||||
isinstance(other, URL)
|
||||
and other.scheme == self.scheme
|
||||
and other.host == self.host
|
||||
and other.port == self.port
|
||||
and other.target == self.target
|
||||
)
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
if self.port is None:
|
||||
return b"%b://%b%b" % (self.scheme, self.host, self.target)
|
||||
return b"%b://%b:%d%b" % (self.scheme, self.host, self.port, self.target)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"{self.__class__.__name__}(scheme={self.scheme!r}, "
|
||||
f"host={self.host!r}, port={self.port!r}, target={self.target!r})"
|
||||
)
|
||||
|
||||
|
||||
class Request:
|
||||
"""
|
||||
An HTTP request.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
method: bytes | str,
|
||||
url: URL | bytes | str,
|
||||
*,
|
||||
headers: HeaderTypes = None,
|
||||
content: bytes
|
||||
| typing.Iterable[bytes]
|
||||
| typing.AsyncIterable[bytes]
|
||||
| None = None,
|
||||
extensions: Extensions | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Parameters:
|
||||
method: The HTTP request method, either as a string or bytes.
|
||||
For example: `GET`.
|
||||
url: The request URL, either as a `URL` instance, or as a string or bytes.
|
||||
For example: `"https://www.example.com".`
|
||||
headers: The HTTP request headers.
|
||||
content: The content of the request body.
|
||||
extensions: A dictionary of optional extra information included on
|
||||
the request. Possible keys include `"timeout"`, and `"trace"`.
|
||||
"""
|
||||
self.method: bytes = enforce_bytes(method, name="method")
|
||||
self.url: URL = enforce_url(url, name="url")
|
||||
self.headers: list[tuple[bytes, bytes]] = enforce_headers(
|
||||
headers, name="headers"
|
||||
)
|
||||
self.stream: typing.Iterable[bytes] | typing.AsyncIterable[bytes] = (
|
||||
enforce_stream(content, name="content")
|
||||
)
|
||||
self.extensions = {} if extensions is None else extensions
|
||||
|
||||
if "target" in self.extensions:
|
||||
self.url = URL(
|
||||
scheme=self.url.scheme,
|
||||
host=self.url.host,
|
||||
port=self.url.port,
|
||||
target=self.extensions["target"],
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__.__name__} [{self.method!r}]>"
|
||||
|
||||
|
||||
class Response:
|
||||
"""
|
||||
An HTTP response.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
status: int,
|
||||
*,
|
||||
headers: HeaderTypes = None,
|
||||
content: bytes
|
||||
| typing.Iterable[bytes]
|
||||
| typing.AsyncIterable[bytes]
|
||||
| None = None,
|
||||
extensions: Extensions | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Parameters:
|
||||
status: The HTTP status code of the response. For example `200`.
|
||||
headers: The HTTP response headers.
|
||||
content: The content of the response body.
|
||||
extensions: A dictionary of optional extra information included on
|
||||
the responseself.Possible keys include `"http_version"`,
|
||||
`"reason_phrase"`, and `"network_stream"`.
|
||||
"""
|
||||
self.status: int = status
|
||||
self.headers: list[tuple[bytes, bytes]] = enforce_headers(
|
||||
headers, name="headers"
|
||||
)
|
||||
self.stream: typing.Iterable[bytes] | typing.AsyncIterable[bytes] = (
|
||||
enforce_stream(content, name="content")
|
||||
)
|
||||
self.extensions = {} if extensions is None else extensions
|
||||
|
||||
self._stream_consumed = False
|
||||
|
||||
@property
|
||||
def content(self) -> bytes:
|
||||
if not hasattr(self, "_content"):
|
||||
if isinstance(self.stream, typing.Iterable):
|
||||
raise RuntimeError(
|
||||
"Attempted to access 'response.content' on a streaming response. "
|
||||
"Call 'response.read()' first."
|
||||
)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"Attempted to access 'response.content' on a streaming response. "
|
||||
"Call 'await response.aread()' first."
|
||||
)
|
||||
return self._content
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__.__name__} [{self.status}]>"
|
||||
|
||||
# Sync interface...
|
||||
|
||||
def read(self) -> bytes:
|
||||
if not isinstance(self.stream, typing.Iterable): # pragma: nocover
|
||||
raise RuntimeError(
|
||||
"Attempted to read an asynchronous response using 'response.read()'. "
|
||||
"You should use 'await response.aread()' instead."
|
||||
)
|
||||
if not hasattr(self, "_content"):
|
||||
self._content = b"".join([part for part in self.iter_stream()])
|
||||
return self._content
|
||||
|
||||
def iter_stream(self) -> typing.Iterator[bytes]:
|
||||
if not isinstance(self.stream, typing.Iterable): # pragma: nocover
|
||||
raise RuntimeError(
|
||||
"Attempted to stream an asynchronous response using 'for ... in "
|
||||
"response.iter_stream()'. "
|
||||
"You should use 'async for ... in response.aiter_stream()' instead."
|
||||
)
|
||||
if self._stream_consumed:
|
||||
raise RuntimeError(
|
||||
"Attempted to call 'for ... in response.iter_stream()' more than once."
|
||||
)
|
||||
self._stream_consumed = True
|
||||
for chunk in self.stream:
|
||||
yield chunk
|
||||
|
||||
def close(self) -> None:
|
||||
if not isinstance(self.stream, typing.Iterable): # pragma: nocover
|
||||
raise RuntimeError(
|
||||
"Attempted to close an asynchronous response using 'response.close()'. "
|
||||
"You should use 'await response.aclose()' instead."
|
||||
)
|
||||
if hasattr(self.stream, "close"):
|
||||
self.stream.close()
|
||||
|
||||
# Async interface...
|
||||
|
||||
async def aread(self) -> bytes:
|
||||
if not isinstance(self.stream, typing.AsyncIterable): # pragma: nocover
|
||||
raise RuntimeError(
|
||||
"Attempted to read an synchronous response using "
|
||||
"'await response.aread()'. "
|
||||
"You should use 'response.read()' instead."
|
||||
)
|
||||
if not hasattr(self, "_content"):
|
||||
self._content = b"".join([part async for part in self.aiter_stream()])
|
||||
return self._content
|
||||
|
||||
async def aiter_stream(self) -> typing.AsyncIterator[bytes]:
|
||||
if not isinstance(self.stream, typing.AsyncIterable): # pragma: nocover
|
||||
raise RuntimeError(
|
||||
"Attempted to stream an synchronous response using 'async for ... in "
|
||||
"response.aiter_stream()'. "
|
||||
"You should use 'for ... in response.iter_stream()' instead."
|
||||
)
|
||||
if self._stream_consumed:
|
||||
raise RuntimeError(
|
||||
"Attempted to call 'async for ... in response.aiter_stream()' "
|
||||
"more than once."
|
||||
)
|
||||
self._stream_consumed = True
|
||||
async for chunk in self.stream:
|
||||
yield chunk
|
||||
|
||||
async def aclose(self) -> None:
|
||||
if not isinstance(self.stream, typing.AsyncIterable): # pragma: nocover
|
||||
raise RuntimeError(
|
||||
"Attempted to close a synchronous response using "
|
||||
"'await response.aclose()'. "
|
||||
"You should use 'response.close()' instead."
|
||||
)
|
||||
if hasattr(self.stream, "aclose"):
|
||||
await self.stream.aclose()
|
||||
|
||||
|
||||
class Proxy:
|
||||
def __init__(
|
||||
self,
|
||||
url: URL | bytes | str,
|
||||
auth: tuple[bytes | str, bytes | str] | None = None,
|
||||
headers: HeadersAsMapping | HeadersAsSequence | None = None,
|
||||
ssl_context: ssl.SSLContext | None = None,
|
||||
):
|
||||
self.url = enforce_url(url, name="url")
|
||||
self.headers = enforce_headers(headers, name="headers")
|
||||
self.ssl_context = ssl_context
|
||||
|
||||
if auth is not None:
|
||||
username = enforce_bytes(auth[0], name="auth")
|
||||
password = enforce_bytes(auth[1], name="auth")
|
||||
userpass = username + b":" + password
|
||||
authorization = b"Basic " + base64.b64encode(userpass)
|
||||
self.auth: tuple[bytes, bytes] | None = (username, password)
|
||||
self.headers = [(b"Proxy-Authorization", authorization)] + self.headers
|
||||
else:
|
||||
self.auth = None
|
Reference in New Issue
Block a user