mirror of https://github.com/scrapy/scrapy.git
Improve type hints for copy() and replace() in Request and Response.
This commit is contained in:
parent
b4acf5c827
commit
a6cee787dd
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import io
|
||||
import zlib
|
||||
from typing import TYPE_CHECKING, List, Optional, Union
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
||||
|
||||
from scrapy import Request, Spider
|
||||
from scrapy.crawler import Crawler
|
||||
|
|
@ -74,12 +74,12 @@ class HttpCompressionMiddleware:
|
|||
respcls = responsetypes.from_args(
|
||||
headers=response.headers, url=response.url, body=decoded_body
|
||||
)
|
||||
kwargs = dict(cls=respcls, body=decoded_body)
|
||||
kwargs: Dict[str, Any] = dict(body=decoded_body)
|
||||
if issubclass(respcls, TextResponse):
|
||||
# force recalculating the encoding until we make sure the
|
||||
# responsetypes guessing is reliable
|
||||
kwargs["encoding"] = None
|
||||
response = response.replace(**kwargs)
|
||||
response = response.replace(cls=respcls, **kwargs)
|
||||
if not content_encoding:
|
||||
del response.headers["Content-Encoding"]
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ def _build_redirect_request(
|
|||
redirect_request = source_request.replace(
|
||||
url=url,
|
||||
**kwargs,
|
||||
cls=None,
|
||||
cookies=None,
|
||||
)
|
||||
if "Cookie" in redirect_request.headers:
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@ requests in Scrapy.
|
|||
|
||||
See documentation in docs/topics/request-response.rst
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
AnyStr,
|
||||
Callable,
|
||||
|
|
@ -19,7 +22,7 @@ from typing import (
|
|||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
overload,
|
||||
)
|
||||
|
||||
from w3lib.url import safe_url_string
|
||||
|
|
@ -31,6 +34,11 @@ from scrapy.utils.python import to_bytes
|
|||
from scrapy.utils.trackref import object_ref
|
||||
from scrapy.utils.url import escape_ajax
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# typing.Self requires Python 3.11
|
||||
from typing_extensions import Self
|
||||
|
||||
|
||||
RequestTypeVar = TypeVar("RequestTypeVar", bound="Request")
|
||||
|
||||
|
||||
|
|
@ -173,23 +181,36 @@ class Request(object_ref):
|
|||
def __repr__(self) -> str:
|
||||
return f"<{self.method} {self.url}>"
|
||||
|
||||
def copy(self) -> "Request":
|
||||
def copy(self) -> Self:
|
||||
return self.replace()
|
||||
|
||||
def replace(self, *args: Any, **kwargs: Any) -> "Request":
|
||||
@overload
|
||||
def replace(
|
||||
self, *args: Any, cls: Type[RequestTypeVar], **kwargs: Any
|
||||
) -> RequestTypeVar:
|
||||
...
|
||||
|
||||
@overload
|
||||
def replace(self, *args: Any, cls: None = None, **kwargs: Any) -> Self:
|
||||
...
|
||||
|
||||
def replace(
|
||||
self, *args: Any, cls: Optional[Type[Request]] = None, **kwargs: Any
|
||||
) -> Request:
|
||||
"""Create a new Request with the same attributes except for those given new values"""
|
||||
for x in self.attributes:
|
||||
kwargs.setdefault(x, getattr(self, x))
|
||||
cls = kwargs.pop("cls", self.__class__)
|
||||
return cast(Request, cls(*args, **kwargs))
|
||||
if cls is None:
|
||||
cls = self.__class__
|
||||
return cls(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_curl(
|
||||
cls: Type[RequestTypeVar],
|
||||
cls,
|
||||
curl_command: str,
|
||||
ignore_unknown_options: bool = True,
|
||||
**kwargs: Any,
|
||||
) -> RequestTypeVar:
|
||||
) -> Self:
|
||||
"""Create a Request object from a string containing a `cURL
|
||||
<https://curl.haxx.se/>`_ command. It populates the HTTP method, the
|
||||
URL, the headers, the cookies and the body. It accepts the same
|
||||
|
|
@ -221,7 +242,7 @@ class Request(object_ref):
|
|||
request_kwargs.update(kwargs)
|
||||
return cls(**request_kwargs)
|
||||
|
||||
def to_dict(self, *, spider: Optional["scrapy.Spider"] = None) -> Dict[str, Any]:
|
||||
def to_dict(self, *, spider: Optional[scrapy.Spider] = None) -> Dict[str, Any]:
|
||||
"""Return a dictionary containing the Request's data.
|
||||
|
||||
Use :func:`~scrapy.utils.request.request_from_dict` to convert back into a :class:`~scrapy.Request` object.
|
||||
|
|
|
|||
|
|
@ -5,12 +5,18 @@ This module implements the JsonRequest class which is a more convenient class
|
|||
See documentation in docs/topics/request-response.rst
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import json
|
||||
import warnings
|
||||
from typing import Any, Optional, Tuple
|
||||
from typing import TYPE_CHECKING, Any, Optional, Tuple, Type, overload
|
||||
|
||||
from scrapy.http.request import Request
|
||||
from scrapy.http.request import Request, RequestTypeVar
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# typing.Self requires Python 3.11
|
||||
from typing_extensions import Self
|
||||
|
||||
|
||||
class JsonRequest(Request):
|
||||
|
|
@ -44,7 +50,19 @@ class JsonRequest(Request):
|
|||
def dumps_kwargs(self) -> dict:
|
||||
return self._dumps_kwargs
|
||||
|
||||
def replace(self, *args: Any, **kwargs: Any) -> Request:
|
||||
@overload
|
||||
def replace(
|
||||
self, *args: Any, cls: Type[RequestTypeVar], **kwargs: Any
|
||||
) -> RequestTypeVar:
|
||||
...
|
||||
|
||||
@overload
|
||||
def replace(self, *args: Any, cls: None = None, **kwargs: Any) -> Self:
|
||||
...
|
||||
|
||||
def replace(
|
||||
self, *args: Any, cls: Optional[Type[Request]] = None, **kwargs: Any
|
||||
) -> Request:
|
||||
body_passed = kwargs.get("body", None) is not None
|
||||
data = kwargs.pop("data", None)
|
||||
data_passed = data is not None
|
||||
|
|
@ -54,7 +72,7 @@ class JsonRequest(Request):
|
|||
elif not body_passed and data_passed:
|
||||
kwargs["body"] = self._dumps(data)
|
||||
|
||||
return super().replace(*args, **kwargs)
|
||||
return super().replace(*args, cls=cls, **kwargs)
|
||||
|
||||
def _dumps(self, data: dict) -> str:
|
||||
"""Convert to JSON"""
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ from typing import (
|
|||
Mapping,
|
||||
Optional,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
overload,
|
||||
)
|
||||
from urllib.parse import urljoin
|
||||
|
||||
|
|
@ -33,9 +35,15 @@ from scrapy.link import Link
|
|||
from scrapy.utils.trackref import object_ref
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# typing.Self requires Python 3.11
|
||||
from typing_extensions import Self
|
||||
|
||||
from scrapy.selector import SelectorList
|
||||
|
||||
|
||||
ResponseTypeVar = TypeVar("ResponseTypeVar", bound="Response")
|
||||
|
||||
|
||||
class Response(object_ref):
|
||||
"""An object that represents an HTTP response, which is usually
|
||||
downloaded (by the Downloader) and fed to the Spiders for processing.
|
||||
|
|
@ -132,16 +140,29 @@ class Response(object_ref):
|
|||
def __repr__(self) -> str:
|
||||
return f"<{self.status} {self.url}>"
|
||||
|
||||
def copy(self) -> Response:
|
||||
def copy(self) -> Self:
|
||||
"""Return a copy of this Response"""
|
||||
return self.replace()
|
||||
|
||||
def replace(self, *args: Any, **kwargs: Any) -> Response:
|
||||
@overload
|
||||
def replace(
|
||||
self, *args: Any, cls: Type[ResponseTypeVar], **kwargs: Any
|
||||
) -> ResponseTypeVar:
|
||||
...
|
||||
|
||||
@overload
|
||||
def replace(self, *args: Any, cls: None = None, **kwargs: Any) -> Self:
|
||||
...
|
||||
|
||||
def replace(
|
||||
self, *args: Any, cls: Optional[Type[Response]] = None, **kwargs: Any
|
||||
) -> Response:
|
||||
"""Create a new Response with the same attributes except for those given new values"""
|
||||
for x in self.attributes:
|
||||
kwargs.setdefault(x, getattr(self, x))
|
||||
cls = kwargs.pop("cls", self.__class__)
|
||||
return cast(Response, cls(*args, **kwargs))
|
||||
if cls is None:
|
||||
cls = self.__class__
|
||||
return cls(*args, **kwargs)
|
||||
|
||||
def urljoin(self, url: str) -> str:
|
||||
"""Join this Response's url with a possible relative url to form an
|
||||
|
|
|
|||
Loading…
Reference in New Issue