|
@@ -1,9 +1,6 @@
|
|
|
-import http
|
|
|
import inspect
|
|
|
import json as json_module
|
|
|
import logging
|
|
|
-import socket
|
|
|
-from collections import namedtuple
|
|
|
from functools import partialmethod
|
|
|
from functools import wraps
|
|
|
from http import client
|
|
@@ -18,12 +15,14 @@ from typing import Iterable
|
|
|
from typing import Iterator
|
|
|
from typing import List
|
|
|
from typing import Mapping
|
|
|
+from typing import NamedTuple
|
|
|
from typing import Optional
|
|
|
from typing import Sequence
|
|
|
from typing import Sized
|
|
|
from typing import Tuple
|
|
|
from typing import Type
|
|
|
from typing import Union
|
|
|
+from typing import overload
|
|
|
from warnings import warn
|
|
|
|
|
|
import yaml
|
|
@@ -96,7 +95,11 @@ if TYPE_CHECKING: # pragma: no cover
|
|
|
]
|
|
|
|
|
|
|
|
|
-Call = namedtuple("Call", ["request", "response"])
|
|
|
+class Call(NamedTuple):
|
|
|
+ request: "PreparedRequest"
|
|
|
+ response: "_Body"
|
|
|
+
|
|
|
+
|
|
|
_real_send = HTTPAdapter.send
|
|
|
_UNSET = object()
|
|
|
|
|
@@ -241,6 +244,14 @@ class CallList(Sequence[Any], Sized):
|
|
|
def __len__(self) -> int:
|
|
|
return len(self._calls)
|
|
|
|
|
|
+ @overload
|
|
|
+ def __getitem__(self, idx: int) -> Call:
|
|
|
+ ...
|
|
|
+
|
|
|
+ @overload
|
|
|
+ def __getitem__(self, idx: slice) -> List[Call]:
|
|
|
+ ...
|
|
|
+
|
|
|
def __getitem__(self, idx: Union[int, slice]) -> Union[Call, List[Call]]:
|
|
|
return self._calls[idx]
|
|
|
|
|
@@ -392,6 +403,9 @@ class BaseResponse:
|
|
|
self._calls: CallList = CallList()
|
|
|
self.passthrough = passthrough
|
|
|
|
|
|
+ self.status: int = 200
|
|
|
+ self.body: "_Body" = ""
|
|
|
+
|
|
|
def __eq__(self, other: Any) -> bool:
|
|
|
if not isinstance(other, BaseResponse):
|
|
|
return False
|
|
@@ -519,25 +533,38 @@ def _form_response(
|
|
|
headers: Optional[Mapping[str, str]],
|
|
|
status: int,
|
|
|
) -> HTTPResponse:
|
|
|
- dummy_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
- orig_response = http.client.HTTPResponse(sock=dummy_socket)
|
|
|
"""
|
|
|
+ Function to generate `urllib3.response.HTTPResponse` object.
|
|
|
+
|
|
|
The cookie handling functionality of the `requests` library relies on the response object
|
|
|
having an original response object with the headers stored in the `msg` attribute.
|
|
|
Instead of supplying a file-like object of type `HTTPMessage` for the headers, we provide
|
|
|
the headers directly. This approach eliminates the need to parse the headers into a file-like
|
|
|
object and then rely on the library to unparse it back. These additional conversions can
|
|
|
introduce potential errors.
|
|
|
- Therefore, we intentionally ignore type checking for this assignment.
|
|
|
"""
|
|
|
- orig_response.msg = headers # type: ignore[assignment]
|
|
|
|
|
|
+ data = BytesIO()
|
|
|
+ data.close()
|
|
|
+
|
|
|
+ """
|
|
|
+ The type `urllib3.response.HTTPResponse` is incorrect; we should
|
|
|
+ use `http.client.HTTPResponse` instead. However, changing this requires opening
|
|
|
+ a real socket to imitate the object. This may not be desired, as some users may
|
|
|
+ want to completely restrict network access in their tests.
|
|
|
+ See https://github.com/getsentry/responses/issues/691
|
|
|
+ """
|
|
|
+ orig_response = HTTPResponse(
|
|
|
+ body=data, # required to avoid "ValueError: Unable to determine whether fp is closed."
|
|
|
+ msg=headers, # type: ignore[arg-type]
|
|
|
+ preload_content=False,
|
|
|
+ )
|
|
|
return HTTPResponse(
|
|
|
status=status,
|
|
|
reason=client.responses.get(status, None),
|
|
|
body=body,
|
|
|
headers=headers,
|
|
|
- original_response=orig_response,
|
|
|
+ original_response=orig_response, # type: ignore[arg-type] # See comment above
|
|
|
preload_content=False,
|
|
|
)
|
|
|
|
|
@@ -556,6 +583,8 @@ class Response(BaseResponse):
|
|
|
auto_calculate_content_length: bool = False,
|
|
|
**kwargs: Any,
|
|
|
) -> None:
|
|
|
+ super().__init__(method, url, **kwargs)
|
|
|
+
|
|
|
# if we were passed a `json` argument,
|
|
|
# override the body and content_type
|
|
|
if json is not None:
|
|
@@ -583,7 +612,6 @@ class Response(BaseResponse):
|
|
|
self.stream: Optional[bool] = stream
|
|
|
self.content_type: str = content_type # type: ignore[assignment]
|
|
|
self.auto_calculate_content_length: bool = auto_calculate_content_length
|
|
|
- super().__init__(method, url, **kwargs)
|
|
|
|
|
|
def get_response(self, request: "PreparedRequest") -> HTTPResponse:
|
|
|
if self.body and isinstance(self.body, Exception):
|
|
@@ -628,6 +656,8 @@ class CallbackResponse(BaseResponse):
|
|
|
content_type: Optional[str] = "text/plain",
|
|
|
**kwargs: Any,
|
|
|
) -> None:
|
|
|
+ super().__init__(method, url, **kwargs)
|
|
|
+
|
|
|
self.callback = callback
|
|
|
|
|
|
if stream is not None:
|
|
@@ -637,7 +667,6 @@ class CallbackResponse(BaseResponse):
|
|
|
)
|
|
|
self.stream: Optional[bool] = stream
|
|
|
self.content_type: Optional[str] = content_type
|
|
|
- super().__init__(method, url, **kwargs)
|
|
|
|
|
|
def get_response(self, request: "PreparedRequest") -> HTTPResponse:
|
|
|
headers = self.get_headers()
|
|
@@ -957,6 +986,22 @@ class RequestsMock:
|
|
|
self.reset()
|
|
|
return success
|
|
|
|
|
|
+ @overload
|
|
|
+ def activate(self, func: "_F" = ...) -> "_F":
|
|
|
+ """Overload for scenario when 'responses.activate' is used."""
|
|
|
+
|
|
|
+ @overload
|
|
|
+ def activate( # type: ignore[misc]
|
|
|
+ self,
|
|
|
+ *,
|
|
|
+ registry: Type[Any] = ...,
|
|
|
+ assert_all_requests_are_fired: bool = ...,
|
|
|
+ ) -> Callable[["_F"], "_F"]:
|
|
|
+ """Overload for scenario when
|
|
|
+ 'responses.activate(registry=, assert_all_requests_are_fired=True)' is used.
|
|
|
+ See https://github.com/getsentry/responses/pull/469 for more details
|
|
|
+ """
|
|
|
+
|
|
|
def activate(
|
|
|
self,
|
|
|
func: Optional["_F"] = None,
|