mirror of https://github.com/scrapy/scrapy.git
Undeprecated the basic FormRequest API (#7671)
This commit is contained in:
parent
edc353c975
commit
cf5607f8bc
|
|
@ -911,6 +911,11 @@ Request subclasses
|
||||||
Here is the list of built-in :class:`~scrapy.Request` subclasses. You can also subclass
|
Here is the list of built-in :class:`~scrapy.Request` subclasses. You can also subclass
|
||||||
it to implement your own custom functionality.
|
it to implement your own custom functionality.
|
||||||
|
|
||||||
|
FormRequest
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. autoclass:: scrapy.FormRequest
|
||||||
|
|
||||||
JsonRequest
|
JsonRequest
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,9 @@ Use this module (instead of the more specific ones) when importing Headers,
|
||||||
Request and Response outside this module.
|
Request and Response outside this module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from warnings import catch_warnings, filterwarnings
|
|
||||||
|
|
||||||
from scrapy.exceptions import ScrapyDeprecationWarning
|
|
||||||
from scrapy.http.headers import Headers
|
from scrapy.http.headers import Headers
|
||||||
from scrapy.http.request import Request
|
from scrapy.http.request import Request
|
||||||
|
from scrapy.http.request.form import FormRequest
|
||||||
from scrapy.http.request.json_request import JsonRequest
|
from scrapy.http.request.json_request import JsonRequest
|
||||||
from scrapy.http.request.rpc import XmlRpcRequest
|
from scrapy.http.request.rpc import XmlRpcRequest
|
||||||
from scrapy.http.response import Response
|
from scrapy.http.response import Response
|
||||||
|
|
@ -17,19 +15,6 @@ from scrapy.http.response.html import HtmlResponse
|
||||||
from scrapy.http.response.json import JsonResponse
|
from scrapy.http.response.json import JsonResponse
|
||||||
from scrapy.http.response.text import TextResponse
|
from scrapy.http.response.text import TextResponse
|
||||||
from scrapy.http.response.xml import XmlResponse
|
from scrapy.http.response.xml import XmlResponse
|
||||||
from scrapy.utils.deprecate import create_deprecated_class
|
|
||||||
|
|
||||||
with catch_warnings():
|
|
||||||
filterwarnings("ignore", category=ScrapyDeprecationWarning)
|
|
||||||
|
|
||||||
from scrapy.http.request.form import FormRequest as _FormRequest
|
|
||||||
|
|
||||||
FormRequest = create_deprecated_class(
|
|
||||||
name="FormRequest",
|
|
||||||
new_class=_FormRequest,
|
|
||||||
subclass_warn_message="{cls} inherits from deprecated class {old}, use the form2request library instead.",
|
|
||||||
instance_warn_message="{cls} is deprecated, use the form2request library instead.",
|
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"FormRequest",
|
"FormRequest",
|
||||||
|
|
|
||||||
|
|
@ -32,19 +32,61 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
from scrapy.http.response.text import TextResponse
|
from scrapy.http.response.text import TextResponse
|
||||||
|
|
||||||
warn(
|
|
||||||
"The entire scrapy.http.request.form module is deprecated. Use the "
|
|
||||||
"form2request library instead.",
|
|
||||||
ScrapyDeprecationWarning,
|
|
||||||
stacklevel=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
FormdataVType: TypeAlias = str | Iterable[str]
|
FormdataVType: TypeAlias = str | Iterable[str]
|
||||||
FormdataKVType: TypeAlias = tuple[str, FormdataVType]
|
FormdataKVType: TypeAlias = tuple[str, FormdataVType]
|
||||||
FormdataType: TypeAlias = dict[str, FormdataVType] | list[FormdataKVType] | None
|
FormdataType: TypeAlias = dict[str, FormdataVType] | list[FormdataKVType] | None
|
||||||
|
|
||||||
|
|
||||||
class FormRequest(Request):
|
class FormRequest(Request):
|
||||||
|
"""A :class:`~scrapy.Request` subclass with a ``formdata`` parameter that
|
||||||
|
url-encodes the given data and assigns it to the request, which makes it
|
||||||
|
convenient to send arbitrary form data via HTTP POST or GET without an HTML
|
||||||
|
``<form>`` element to parse.
|
||||||
|
|
||||||
|
.. note:: To build a request from an HTML ``<form>`` element found in a
|
||||||
|
response, use :doc:`form2request <form2request:index>` instead. See
|
||||||
|
:ref:`form`.
|
||||||
|
|
||||||
|
The remaining arguments are the same as for the :class:`~scrapy.Request`
|
||||||
|
class and are not documented here.
|
||||||
|
|
||||||
|
:param formdata: a dictionary (or iterable of (key, value) tuples)
|
||||||
|
containing HTML form data which will be url-encoded. If
|
||||||
|
:attr:`~scrapy.Request.method` is not given and ``formdata`` is
|
||||||
|
provided, the method is set to ``"POST"`` and the data is assigned to
|
||||||
|
the request body; if the method is ``"GET"``, the data is added to the
|
||||||
|
URL query string instead.
|
||||||
|
:type formdata: dict or collections.abc.Iterable
|
||||||
|
|
||||||
|
To send data via HTTP POST, simulating an HTML form submission, return a
|
||||||
|
:class:`~scrapy.FormRequest` object from your spider:
|
||||||
|
|
||||||
|
.. skip: next
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
return [
|
||||||
|
FormRequest(
|
||||||
|
url="http://www.example.com/post/action",
|
||||||
|
formdata={"name": "John Doe", "age": "27"},
|
||||||
|
callback=self.after_post,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
To send the data in the URL query string instead, use the ``GET`` method:
|
||||||
|
|
||||||
|
.. skip: next
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
return [
|
||||||
|
FormRequest(
|
||||||
|
url="http://www.example.com/search",
|
||||||
|
method="GET",
|
||||||
|
formdata={"q": "keyword", "page": "1"},
|
||||||
|
callback=self.parse_results,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
valid_form_methods: ClassVar[list[str]] = ["GET", "POST"]
|
valid_form_methods: ClassVar[list[str]] = ["GET", "POST"]
|
||||||
|
|
@ -84,6 +126,13 @@ class FormRequest(Request):
|
||||||
formcss: str | None = None,
|
formcss: str | None = None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
warn(
|
||||||
|
"FormRequest.from_response() is deprecated. Use the form2request "
|
||||||
|
"library instead.",
|
||||||
|
ScrapyDeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
|
||||||
kwargs.setdefault("encoding", response.encoding)
|
kwargs.setdefault("encoding", response.encoding)
|
||||||
|
|
||||||
if formcss is not None:
|
if formcss is not None:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import warnings
|
||||||
from urllib.parse import parse_qs, unquote_to_bytes
|
from urllib.parse import parse_qs, unquote_to_bytes
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from scrapy.exceptions import ScrapyDeprecationWarning
|
||||||
from scrapy.http import FormRequest, HtmlResponse
|
from scrapy.http import FormRequest, HtmlResponse
|
||||||
from scrapy.utils.httpobj import urlparse_cached
|
from scrapy.utils.httpobj import urlparse_cached
|
||||||
from scrapy.utils.python import to_unicode
|
from scrapy.utils.python import to_unicode
|
||||||
|
|
@ -26,15 +28,39 @@ def _qs(req, encoding="utf-8", to_unicode=False):
|
||||||
return parse_qs(uqs, True)
|
return parse_qs(uqs, True)
|
||||||
|
|
||||||
|
|
||||||
|
# FormRequest.from_response() is deprecated in favor of form2request, so the
|
||||||
|
# many tests below that exercise it ignore the resulting deprecation warning.
|
||||||
@pytest.mark.filterwarnings("ignore::scrapy.exceptions.ScrapyDeprecationWarning")
|
@pytest.mark.filterwarnings("ignore::scrapy.exceptions.ScrapyDeprecationWarning")
|
||||||
class TestFormRequest(TestRequest):
|
class TestFormRequest(TestRequest):
|
||||||
request_class = FormRequest # type: ignore[assignment]
|
request_class = FormRequest
|
||||||
|
|
||||||
def assertQueryEqual(self, first, second, msg=None):
|
def assertQueryEqual(self, first, second, msg=None):
|
||||||
first = to_unicode(first).split("&")
|
first = to_unicode(first).split("&")
|
||||||
second = to_unicode(second).split("&")
|
second = to_unicode(second).split("&")
|
||||||
assert sorted(first) == sorted(second), msg
|
assert sorted(first) == sorted(second), msg
|
||||||
|
|
||||||
|
def test_init_not_deprecated(self):
|
||||||
|
# Building a request directly from form data is not deprecated.
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("error", ScrapyDeprecationWarning)
|
||||||
|
self.request_class(
|
||||||
|
"http://www.example.com", formdata={"a": "1"}, method="POST"
|
||||||
|
)
|
||||||
|
self.request_class(
|
||||||
|
"http://www.example.com", method="GET", formdata={"a": "1"}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_from_response_deprecated(self):
|
||||||
|
response = _buildresponse(
|
||||||
|
"""<form action="post.php" method="POST">
|
||||||
|
<input type="hidden" name="one" value="1">
|
||||||
|
</form>"""
|
||||||
|
)
|
||||||
|
with pytest.warns(
|
||||||
|
ScrapyDeprecationWarning, match=r"FormRequest\.from_response\(\)"
|
||||||
|
):
|
||||||
|
self.request_class.from_response(response)
|
||||||
|
|
||||||
def test_empty_formdata(self):
|
def test_empty_formdata(self):
|
||||||
r1 = self.request_class("http://www.example.com", formdata={})
|
r1 = self.request_class("http://www.example.com", formdata={})
|
||||||
assert r1.body == b""
|
assert r1.body == b""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue