Merge remote-tracking branch 'origin/master' into check-crawl-errors

This commit is contained in:
Adrian Chaves 2026-08-12 22:01:30 +02:00
commit b6c0dfe52d
11 changed files with 34 additions and 21 deletions

View File

@ -47,7 +47,7 @@ if not H2_ENABLED:
collect_ignore.extend(
(
"scrapy/core/downloader/handlers/http2.py",
*_py_files("scrapy/core/http2"),
*_py_files("scrapy/core/_http2"),
)
)
@ -107,6 +107,14 @@ def pytest_configure(config):
install_reactor_import_hook()
def pytest_collection_modifyitems(items):
for item in items:
if item.get_closest_marker("requires_internet"):
# Requests to real websites fail every now and then in CI for
# reasons unrelated to the code under test.
item.add_marker(pytest.mark.flaky(reruns=2, reruns_delay=5))
def pytest_runtest_setup(item):
# Skip tests based on reactor markers
reactor = item.config.getoption("--reactor")

View File

@ -187,12 +187,6 @@ If you want to use this handler you need to replace the default one for the
Features and limitations
^^^^^^^^^^^^^^^^^^^^^^^^
.. warning::
This handler is experimental, and not yet recommended for production
environments. Future Scrapy versions may introduce related changes without
a deprecation period or warning.
=========================== ================================================
HTTP proxies No (not implemented)
SOCKS proxies No (not supported by the library)

View File

@ -14,8 +14,8 @@ from twisted.web.client import (
)
from twisted.web.error import SchemeNotSupported
from scrapy.core._http2.protocol import H2ClientFactory, H2ClientProtocol
from scrapy.core.downloader.contextfactory import _AcceptableProtocolsContextFactory
from scrapy.core.http2.protocol import H2ClientFactory, H2ClientProtocol
if TYPE_CHECKING:
from twisted.internet.base import ReactorBase

View File

@ -31,7 +31,7 @@ from twisted.internet.ssl import Certificate
from twisted.protocols.policies import TimeoutMixin
from zope.interface import implementer
from scrapy.core.http2.stream import Stream, StreamCloseReason
from scrapy.core._http2.stream import Stream, StreamCloseReason
from scrapy.exceptions import DownloadTimeoutError
from scrapy.http import Request, Response
from scrapy.utils.deprecate import warn_on_deprecated_spider_attribute

View File

@ -27,7 +27,7 @@ from scrapy.utils.httpobj import urlparse_cached
if TYPE_CHECKING:
from collections.abc import Sequence
from scrapy.core.http2.protocol import H2ClientProtocol
from scrapy.core._http2.protocol import H2ClientProtocol
from scrapy.crawler import Crawler
from scrapy.http import Request, Response

View File

@ -4,9 +4,9 @@ from time import monotonic
from typing import TYPE_CHECKING
from urllib.parse import urldefrag
from scrapy.core._http2.agent import H2Agent, H2ConnectionPool
from scrapy.core.downloader.contextfactory import _load_context_factory_from_settings
from scrapy.core.downloader.handlers.base import BaseDownloadHandler
from scrapy.core.http2.agent import H2Agent, H2ConnectionPool
from scrapy.exceptions import (
DownloadTimeoutError,
NotConfigured,

View File

@ -143,7 +143,7 @@ class TestHttp2(H2DownloadHandlerMixin, TestHttpsBase):
response = await download_handler.download_request(request)
assert response.text == actual_content_length
assert (
"scrapy.core.http2.stream",
"scrapy.core._http2.stream",
logging.WARNING,
f"Ignoring bad Content-Length header "
f"{bad_content_length!r} of request {request}, sending "

View File

@ -36,7 +36,7 @@ from tests.mockserver.utils import ssl_context_factory
if TYPE_CHECKING:
from collections.abc import AsyncGenerator, Callable, Coroutine, Generator
from scrapy.core.http2.protocol import H2ClientProtocol
from scrapy.core._http2.protocol import H2ClientProtocol
pytestmark = [
@ -242,7 +242,7 @@ class TestHttps2ClientProtocol:
) -> AsyncGenerator[H2ClientProtocol]:
from twisted.internet import reactor
from scrapy.core.http2.protocol import H2ClientFactory # noqa: PLC0415
from scrapy.core._http2.protocol import H2ClientFactory # noqa: PLC0415
client_options = optionsForClientTLS(
hostname=self.host,
@ -460,7 +460,7 @@ class TestHttps2ClientProtocol:
def test_invalid_negotiated_protocol(
self, server_port: int, client: H2ClientProtocol
) -> Generator[Deferred[Any], Any, None]:
with mock.patch("scrapy.core.http2.protocol.PROTOCOL_NAME", new=b"not-h2"):
with mock.patch("scrapy.core._http2.protocol.PROTOCOL_NAME", new=b"not-h2"):
request = Request(url=self.get_url(server_port, "/status?n=200"))
with pytest.raises(ResponseFailed):
yield make_request_dfd(client, request)
@ -527,7 +527,7 @@ class TestHttps2ClientProtocol:
expected_body: bytes,
caplog: pytest.LogCaptureFixture,
) -> None:
with caplog.at_level("WARNING", "scrapy.core.http2.stream"):
with caplog.at_level("WARNING", "scrapy.core._http2.stream"):
response = await make_request(client, request)
assert response.status == 200
assert response.body == expected_body
@ -605,7 +605,7 @@ class TestHttps2ClientProtocol:
def assert_inactive_stream(failure):
assert failure.check(ResponseFailed) is not None
from scrapy.core.http2.stream import InactiveStreamClosed # noqa: PLC0415
from scrapy.core._http2.stream import InactiveStreamClosed # noqa: PLC0415
assert any(
isinstance(e, InactiveStreamClosed) for e in failure.value.reasons
@ -692,7 +692,7 @@ class TestHttps2ClientProtocol:
@staticmethod
async def _check_invalid_netloc(client: H2ClientProtocol, url: str) -> None:
from scrapy.core.http2.stream import InvalidHostname # noqa: PLC0415
from scrapy.core._http2.stream import InvalidHostname # noqa: PLC0415
request = Request(url)
with pytest.raises(InvalidHostname) as exc_info:
@ -737,7 +737,7 @@ class TestHttps2ClientProtocol:
yield make_request_dfd(client, request)
for err in exc_info.value.reasons:
from scrapy.core.http2.protocol import H2ClientProtocol # noqa: PLC0415
from scrapy.core._http2.protocol import H2ClientProtocol # noqa: PLC0415
if isinstance(err, DownloadTimeoutError):
assert (

View File

@ -1513,6 +1513,12 @@ class TestMitmProxyBase(ABC):
assert "Proxy Authentication Required" in log or "407" in log
# Tests below are rerun on failure (see pytest_collection_modifyitems() in the
# root conftest.py), so an attempt must give up soon enough for a rerun to be
# cheap.
REAL_WEBSITE_SETTINGS = {"DOWNLOAD_TIMEOUT": 30}
class TestRealWebsiteBase(ABC):
@property
@abstractmethod
@ -1537,7 +1543,9 @@ class TestRealWebsiteBase(ABC):
async def get_dh(
self, settings_dict: dict[str, Any] | None = None
) -> AsyncGenerator[DownloadHandlerProtocol]:
crawler = get_crawler(DefaultSpider, settings_dict)
crawler = get_crawler(
DefaultSpider, {**REAL_WEBSITE_SETTINGS, **(settings_dict or {})}
)
crawler.spider = crawler._create_spider()
dh = build_from_crawler(self.download_handler_cls, crawler)
try:
@ -1555,7 +1563,9 @@ class TestRealWebsiteBase(ABC):
@coroutine_test
async def test_download_with_spider(self) -> None:
crawler = get_crawler(SingleRequestSpider, self.settings_dict)
crawler = get_crawler(
SingleRequestSpider, {**REAL_WEBSITE_SETTINGS, **(self.settings_dict or {})}
)
await maybe_deferred_to_future(
crawler.crawl(seed=Request("https://books.toscrape.com/"))
)

View File

@ -44,6 +44,7 @@ deps =
pygments
pytest
pytest-cov >= 7.0.0
pytest-rerunfailures
pytest-timeout
pytest-xdist
sybil >= 1.3.0 # https://github.com/cjw296/sybil/issues/20#issuecomment-605433422