mirror of https://github.com/scrapy/scrapy.git
Enable in-process HTTP tests without a reactor. (#7254)
This commit is contained in:
parent
6e0a0e476a
commit
ccfa052fa1
|
|
@ -93,10 +93,7 @@ def pytest_runtest_setup(item):
|
||||||
# Skip tests based on reactor markers
|
# Skip tests based on reactor markers
|
||||||
reactor = item.config.getoption("--reactor")
|
reactor = item.config.getoption("--reactor")
|
||||||
|
|
||||||
if (
|
if item.get_closest_marker("requires_reactor") and reactor == "none":
|
||||||
item.get_closest_marker("requires_reactor")
|
|
||||||
or item.get_closest_marker("requires_http_handler")
|
|
||||||
) and reactor == "none":
|
|
||||||
pytest.skip('This test is only run when the --reactor value is not "none"')
|
pytest.skip('This test is only run when the --reactor value is not "none"')
|
||||||
|
|
||||||
if item.get_closest_marker("only_asyncio") and reactor not in {"asyncio", "none"}:
|
if item.get_closest_marker("only_asyncio") and reactor not in {"asyncio", "none"}:
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,6 @@ markers = [
|
||||||
"only_asyncio: marks tests that require the asyncio loop to be used",
|
"only_asyncio: marks tests that require the asyncio loop to be used",
|
||||||
"only_not_asyncio: marks tests that require the asyncio loop to not be used",
|
"only_not_asyncio: marks tests that require the asyncio loop to not be used",
|
||||||
"requires_reactor: marks tests that require a reactor",
|
"requires_reactor: marks tests that require a reactor",
|
||||||
"requires_http_handler: marks tests that require a HTTP handler",
|
|
||||||
"requires_uvloop: marks tests as only enabled when uvloop is known to be working",
|
"requires_uvloop: marks tests as only enabled when uvloop is known to be working",
|
||||||
"requires_botocore: marks tests that need botocore (but not boto3)",
|
"requires_botocore: marks tests that need botocore (but not boto3)",
|
||||||
"requires_boto3: marks tests that need botocore and boto3",
|
"requires_boto3: marks tests that need botocore and boto3",
|
||||||
|
|
|
||||||
|
|
@ -131,8 +131,8 @@ def get_reactor_settings() -> dict[str, Any]:
|
||||||
settings["TWISTED_ENABLED"] = False
|
settings["TWISTED_ENABLED"] = False
|
||||||
settings["DOWNLOAD_HANDLERS"] = {
|
settings["DOWNLOAD_HANDLERS"] = {
|
||||||
"ftp": None,
|
"ftp": None,
|
||||||
"http": None,
|
"http": "scrapy.core.downloader.handlers._httpx.HttpxDownloadHandler",
|
||||||
"https": None,
|
"https": "scrapy.core.downloader.handlers._httpx.HttpxDownloadHandler",
|
||||||
}
|
}
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
|
|
@ -208,7 +208,14 @@ def mock_google_cloud_storage() -> tuple[Any, Any, Any]: # pragma: no cover
|
||||||
return (client_mock, bucket_mock, blob_mock)
|
return (client_mock, bucket_mock, blob_mock)
|
||||||
|
|
||||||
|
|
||||||
def get_web_client_agent_req(url: str) -> Deferred[TxResponse]:
|
def get_web_client_agent_req(url: str) -> Deferred[TxResponse]: # pragma: no cover
|
||||||
|
warnings.warn(
|
||||||
|
"The get_web_client_agent_req() function is deprecated"
|
||||||
|
" and will be removed in a future version of Scrapy.",
|
||||||
|
category=ScrapyDeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
|
|
||||||
agent = Agent(reactor)
|
agent = Agent(reactor)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ from scrapy.linkextractors import LinkExtractor
|
||||||
from scrapy.spiders import Spider
|
from scrapy.spiders import Spider
|
||||||
from scrapy.spiders.crawl import CrawlSpider, Rule
|
from scrapy.spiders.crawl import CrawlSpider, Rule
|
||||||
from scrapy.utils.defer import deferred_to_future, maybe_deferred_to_future
|
from scrapy.utils.defer import deferred_to_future, maybe_deferred_to_future
|
||||||
from scrapy.utils.test import get_from_asyncio_queue, get_web_client_agent_req
|
from scrapy.utils.test import get_from_asyncio_queue
|
||||||
|
|
||||||
|
|
||||||
class MockServerSpider(Spider):
|
class MockServerSpider(Spider):
|
||||||
|
|
@ -199,28 +199,24 @@ class AsyncDefDeferredDirectSpider(SimpleSpider):
|
||||||
name = "asyncdef_deferred_direct"
|
name = "asyncdef_deferred_direct"
|
||||||
|
|
||||||
async def parse(self, response):
|
async def parse(self, response):
|
||||||
resp = await get_web_client_agent_req(self.mockserver.url("/status?n=200"))
|
await defer.succeed(None)
|
||||||
yield {"code": resp.code}
|
yield {"code": 200}
|
||||||
|
|
||||||
|
|
||||||
class AsyncDefDeferredWrappedSpider(SimpleSpider):
|
class AsyncDefDeferredWrappedSpider(SimpleSpider):
|
||||||
name = "asyncdef_deferred_wrapped"
|
name = "asyncdef_deferred_wrapped"
|
||||||
|
|
||||||
async def parse(self, response):
|
async def parse(self, response):
|
||||||
resp = await deferred_to_future(
|
await deferred_to_future(defer.succeed(None))
|
||||||
get_web_client_agent_req(self.mockserver.url("/status?n=200"))
|
yield {"code": 200}
|
||||||
)
|
|
||||||
yield {"code": resp.code}
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncDefDeferredMaybeWrappedSpider(SimpleSpider):
|
class AsyncDefDeferredMaybeWrappedSpider(SimpleSpider):
|
||||||
name = "asyncdef_deferred_wrapped"
|
name = "asyncdef_deferred_wrapped"
|
||||||
|
|
||||||
async def parse(self, response):
|
async def parse(self, response):
|
||||||
resp = await maybe_deferred_to_future(
|
await maybe_deferred_to_future(defer.succeed(None))
|
||||||
get_web_client_agent_req(self.mockserver.url("/status?n=200"))
|
yield {"code": 200}
|
||||||
)
|
|
||||||
yield {"code": resp.code}
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncDefAsyncioGenSpider(SimpleSpider):
|
class AsyncDefAsyncioGenSpider(SimpleSpider):
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import pytest
|
|
||||||
|
|
||||||
from scrapy.utils.test import get_crawler
|
from scrapy.utils.test import get_crawler
|
||||||
from tests.mockserver.http import MockServer
|
from tests.mockserver.http import MockServer
|
||||||
from tests.spiders import (
|
from tests.spiders import (
|
||||||
|
|
@ -12,7 +10,6 @@ from tests.spiders import (
|
||||||
from tests.utils.decorators import inline_callbacks_test
|
from tests.utils.decorators import inline_callbacks_test
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestCloseSpider:
|
class TestCloseSpider:
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_class(cls):
|
def setup_class(cls):
|
||||||
|
|
|
||||||
|
|
@ -501,7 +501,6 @@ class TestContractsManager:
|
||||||
assert not self.results.failures
|
assert not self.results.failures
|
||||||
assert self.results.errors
|
assert self.results.errors
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@inline_callbacks_test
|
@inline_callbacks_test
|
||||||
def test_same_url(self):
|
def test_same_url(self):
|
||||||
class TestSameUrlSpider(Spider):
|
class TestSameUrlSpider(Spider):
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,16 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from scrapy.utils.test import get_crawler
|
from scrapy.utils.test import get_crawler
|
||||||
from tests.spiders import SimpleSpider
|
from tests.spiders import SimpleSpider
|
||||||
from tests.utils.decorators import coroutine_test
|
from tests.utils.decorators import coroutine_test
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
import pytest
|
||||||
|
|
||||||
from tests.mockserver.http import MockServer
|
from tests.mockserver.http import MockServer
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_scraper_exception(
|
async def test_scraper_exception(
|
||||||
mockserver: MockServer,
|
mockserver: MockServer,
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ from twisted.internet.ssl import Certificate
|
||||||
from twisted.python.failure import Failure
|
from twisted.python.failure import Failure
|
||||||
|
|
||||||
from scrapy import Spider, signals
|
from scrapy import Spider, signals
|
||||||
from scrapy.crawler import CrawlerRunner
|
from scrapy.crawler import AsyncCrawlerRunner, CrawlerRunner
|
||||||
from scrapy.exceptions import CloseSpider, ScrapyDeprecationWarning, StopDownload
|
from scrapy.exceptions import CloseSpider, ScrapyDeprecationWarning, StopDownload
|
||||||
from scrapy.http import Request
|
from scrapy.http import Request
|
||||||
from scrapy.http.response import Response
|
from scrapy.http.response import Response
|
||||||
from scrapy.utils.defer import maybe_deferred_to_future
|
from scrapy.utils.defer import ensure_awaitable, maybe_deferred_to_future
|
||||||
from scrapy.utils.engine import format_engine_status, get_engine_status
|
from scrapy.utils.engine import format_engine_status, get_engine_status
|
||||||
from scrapy.utils.python import to_unicode
|
from scrapy.utils.python import to_unicode
|
||||||
from scrapy.utils.test import get_crawler, get_reactor_settings
|
from scrapy.utils.test import get_crawler, get_reactor_settings
|
||||||
|
|
@ -61,7 +61,6 @@ if TYPE_CHECKING:
|
||||||
from scrapy.statscollectors import StatsCollector
|
from scrapy.statscollectors import StatsCollector
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler # easier than marking many individual tests
|
|
||||||
class TestCrawl:
|
class TestCrawl:
|
||||||
mockserver: MockServer
|
mockserver: MockServer
|
||||||
|
|
||||||
|
|
@ -402,9 +401,15 @@ with multiples lines
|
||||||
)
|
)
|
||||||
assert "Got response 200" in str(log)
|
assert "Got response 200" in str(log)
|
||||||
|
|
||||||
@inline_callbacks_test
|
@coroutine_test
|
||||||
def test_crawl_multiple(self, caplog: pytest.LogCaptureFixture):
|
async def test_crawl_multiple(self, caplog: pytest.LogCaptureFixture) -> None:
|
||||||
runner = CrawlerRunner(get_reactor_settings())
|
settings_dict = get_reactor_settings()
|
||||||
|
runner_cls = (
|
||||||
|
CrawlerRunner
|
||||||
|
if settings_dict.get("TWISTED_ENABLED", True)
|
||||||
|
else AsyncCrawlerRunner
|
||||||
|
)
|
||||||
|
runner = runner_cls(settings_dict)
|
||||||
runner.crawl(
|
runner.crawl(
|
||||||
SimpleSpider,
|
SimpleSpider,
|
||||||
self.mockserver.url("/status?n=200"),
|
self.mockserver.url("/status?n=200"),
|
||||||
|
|
@ -417,7 +422,7 @@ with multiples lines
|
||||||
)
|
)
|
||||||
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
yield runner.join()
|
await ensure_awaitable(runner.join())
|
||||||
|
|
||||||
self._assert_retried(caplog.text)
|
self._assert_retried(caplog.text)
|
||||||
assert "Got response 200" in caplog.text
|
assert "Got response 200" in caplog.text
|
||||||
|
|
@ -429,7 +434,6 @@ with multiples lines
|
||||||
assert "NotSupported: Unsupported URL scheme 'foo'" in caplog.text
|
assert "NotSupported: Unsupported URL scheme 'foo'" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestCrawlSpider:
|
class TestCrawlSpider:
|
||||||
mockserver: MockServer
|
mockserver: MockServer
|
||||||
|
|
||||||
|
|
@ -636,6 +640,11 @@ class TestCrawlSpider:
|
||||||
yield crawler.crawl(seed=url, mockserver=self.mockserver)
|
yield crawler.crawl(seed=url, mockserver=self.mockserver)
|
||||||
assert crawler.spider.meta["responses"][0].certificate is None
|
assert crawler.spider.meta["responses"][0].certificate is None
|
||||||
|
|
||||||
|
@pytest.mark.xfail(
|
||||||
|
'config.getoption("--reactor") == "none"',
|
||||||
|
reason="Not implemented in HttpxDownloadHandler",
|
||||||
|
strict=True,
|
||||||
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"url",
|
"url",
|
||||||
[
|
[
|
||||||
|
|
@ -643,6 +652,7 @@ class TestCrawlSpider:
|
||||||
pytest.param(
|
pytest.param(
|
||||||
"/status?n=200",
|
"/status?n=200",
|
||||||
marks=pytest.mark.xfail(
|
marks=pytest.mark.xfail(
|
||||||
|
'config.getoption("--reactor") != "none"',
|
||||||
reason="With HTTP11DownloadHandler, responses with no body are returned early and contain no certificate",
|
reason="With HTTP11DownloadHandler, responses with no body are returned early and contain no certificate",
|
||||||
strict=True,
|
strict=True,
|
||||||
),
|
),
|
||||||
|
|
@ -669,6 +679,7 @@ class TestCrawlSpider:
|
||||||
pytest.param(
|
pytest.param(
|
||||||
"/status?n=200",
|
"/status?n=200",
|
||||||
marks=pytest.mark.xfail(
|
marks=pytest.mark.xfail(
|
||||||
|
'config.getoption("--reactor") != "none"',
|
||||||
reason="With HTTP11DownloadHandler, responses with no body are returned early and contain no ip_address",
|
reason="With HTTP11DownloadHandler, responses with no body are returned early and contain no ip_address",
|
||||||
strict=True,
|
strict=True,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -93,10 +93,11 @@ class TestLoad:
|
||||||
crawler = get_crawler(settings_dict={"DOWNLOAD_HANDLERS": handlers})
|
crawler = get_crawler(settings_dict={"DOWNLOAD_HANDLERS": handlers})
|
||||||
dh = DownloadHandlers(crawler)
|
dh = DownloadHandlers(crawler)
|
||||||
assert "scheme" not in dh._schemes
|
assert "scheme" not in dh._schemes
|
||||||
for scheme in handlers: # force load handlers
|
assert dh._get_handler("scheme") is None
|
||||||
dh._get_handler(scheme)
|
|
||||||
assert "scheme" not in dh._handlers
|
assert "scheme" not in dh._handlers
|
||||||
assert "scheme" in dh._notconfigured
|
assert "scheme" in dh._notconfigured
|
||||||
|
# get the handler again to cover the code that gets it from dh._notconfigured
|
||||||
|
assert dh._get_handler("scheme") is None
|
||||||
|
|
||||||
def test_lazy_handlers(self):
|
def test_lazy_handlers(self):
|
||||||
handlers = {"scheme": DummyLazyDH}
|
handlers = {"scheme": DummyLazyDH}
|
||||||
|
|
@ -108,8 +109,8 @@ class TestLoad:
|
||||||
dh = DownloadHandlers(crawler)
|
dh = DownloadHandlers(crawler)
|
||||||
assert "scheme" in dh._schemes
|
assert "scheme" in dh._schemes
|
||||||
assert "scheme" not in dh._handlers
|
assert "scheme" not in dh._handlers
|
||||||
for scheme in handlers: # force load lazy handler
|
handler = dh._get_handler("scheme") # force load lazy handler
|
||||||
dh._get_handler(scheme)
|
assert handler
|
||||||
assert "scheme" in dh._handlers
|
assert "scheme" in dh._handlers
|
||||||
assert "scheme" not in dh._notconfigured
|
assert "scheme" not in dh._notconfigured
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,6 @@ class TestCrawl:
|
||||||
def setup_method(self):
|
def setup_method(self):
|
||||||
self.runner = CrawlerRunner()
|
self.runner = CrawlerRunner()
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@inline_callbacks_test
|
@inline_callbacks_test
|
||||||
def test_delay(self):
|
def test_delay(self):
|
||||||
crawler = get_crawler(DownloaderSlotsSettingsTestSpider)
|
crawler = get_crawler(DownloaderSlotsSettingsTestSpider)
|
||||||
|
|
@ -129,7 +128,6 @@ def test_get_slot_deprecated_spider_arg():
|
||||||
assert slot1 == slot2
|
assert slot1 == slot2
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"priority_queue_class",
|
"priority_queue_class",
|
||||||
[
|
[
|
||||||
|
|
|
||||||
|
|
@ -373,7 +373,6 @@ class TestEngineBase:
|
||||||
|
|
||||||
|
|
||||||
class TestEngine(TestEngineBase):
|
class TestEngine(TestEngineBase):
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_crawler(self, mockserver: MockServer) -> None:
|
async def test_crawler(self, mockserver: MockServer) -> None:
|
||||||
for spider in (
|
for spider in (
|
||||||
|
|
@ -391,7 +390,6 @@ class TestEngine(TestEngineBase):
|
||||||
self._assert_signals_caught(run)
|
self._assert_signals_caught(run)
|
||||||
self._assert_bytes_received(run)
|
self._assert_bytes_received(run)
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_crawler_dupefilter(self, mockserver: MockServer) -> None:
|
async def test_crawler_dupefilter(self, mockserver: MockServer) -> None:
|
||||||
run = CrawlerRun(DupeFilterSpider)
|
run = CrawlerRun(DupeFilterSpider)
|
||||||
|
|
@ -399,14 +397,12 @@ class TestEngine(TestEngineBase):
|
||||||
self._assert_scheduled_requests(run, count=8)
|
self._assert_scheduled_requests(run, count=8)
|
||||||
self._assert_dropped_requests(run)
|
self._assert_dropped_requests(run)
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_crawler_itemerror(self, mockserver: MockServer) -> None:
|
async def test_crawler_itemerror(self, mockserver: MockServer) -> None:
|
||||||
run = CrawlerRun(ItemZeroDivisionErrorSpider)
|
run = CrawlerRun(ItemZeroDivisionErrorSpider)
|
||||||
await run.run(mockserver)
|
await run.run(mockserver)
|
||||||
self._assert_items_error(run)
|
self._assert_items_error(run)
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_crawler_change_close_reason_on_idle(
|
async def test_crawler_change_close_reason_on_idle(
|
||||||
self, mockserver: MockServer
|
self, mockserver: MockServer
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,6 @@ class TestRequestSendOrder:
|
||||||
# Examples from the “Start requests” section of the documentation about
|
# Examples from the “Start requests” section of the documentation about
|
||||||
# spiders.
|
# spiders.
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_lazy(self):
|
async def test_lazy(self):
|
||||||
start_nums = [1, 2, 4]
|
start_nums = [1, 2, 4]
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from scrapy.exceptions import StopDownload
|
from scrapy.exceptions import StopDownload
|
||||||
from tests.test_engine import (
|
from tests.test_engine import (
|
||||||
AttrsItemsSpider,
|
AttrsItemsSpider,
|
||||||
|
|
@ -16,6 +14,8 @@ from tests.test_engine import (
|
||||||
from tests.utils.decorators import coroutine_test
|
from tests.utils.decorators import coroutine_test
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
import pytest
|
||||||
|
|
||||||
from tests.mockserver.http import MockServer
|
from tests.mockserver.http import MockServer
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -26,7 +26,6 @@ class BytesReceivedCrawlerRun(CrawlerRun):
|
||||||
|
|
||||||
|
|
||||||
class TestBytesReceivedEngine(TestEngineBase):
|
class TestBytesReceivedEngine(TestEngineBase):
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_crawler(
|
async def test_crawler(
|
||||||
self, mockserver: MockServer, caplog: pytest.LogCaptureFixture
|
self, mockserver: MockServer, caplog: pytest.LogCaptureFixture
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from scrapy.exceptions import StopDownload
|
from scrapy.exceptions import StopDownload
|
||||||
from tests.test_engine import (
|
from tests.test_engine import (
|
||||||
AttrsItemsSpider,
|
AttrsItemsSpider,
|
||||||
|
|
@ -16,6 +14,8 @@ from tests.test_engine import (
|
||||||
from tests.utils.decorators import coroutine_test
|
from tests.utils.decorators import coroutine_test
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
import pytest
|
||||||
|
|
||||||
from tests.mockserver.http import MockServer
|
from tests.mockserver.http import MockServer
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -26,7 +26,6 @@ class HeadersReceivedCrawlerRun(CrawlerRun):
|
||||||
|
|
||||||
|
|
||||||
class TestHeadersReceivedEngine(TestEngineBase):
|
class TestHeadersReceivedEngine(TestEngineBase):
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_crawler(
|
async def test_crawler(
|
||||||
self, mockserver: MockServer, caplog: pytest.LogCaptureFixture
|
self, mockserver: MockServer, caplog: pytest.LogCaptureFixture
|
||||||
|
|
|
||||||
|
|
@ -849,7 +849,6 @@ class ExceptionJsonItemExporter(JsonItemExporter):
|
||||||
raise RuntimeError("foo")
|
raise RuntimeError("foo")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestFeedExport(TestFeedExportBase):
|
class TestFeedExport(TestFeedExportBase):
|
||||||
async def run_and_export(
|
async def run_and_export(
|
||||||
self, spider_cls: type[Spider], settings: dict[str, Any]
|
self, spider_cls: type[Spider], settings: dict[str, Any]
|
||||||
|
|
@ -1703,6 +1702,7 @@ class TestFeedExport(TestFeedExportBase):
|
||||||
data = await self.exported_no_data(settings)
|
data = await self.exported_no_data(settings)
|
||||||
assert data["csv"] == b""
|
assert data["csv"] == b""
|
||||||
|
|
||||||
|
@pytest.mark.requires_reactor # needs a reactor for BlockingFeedStorage
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_multiple_feeds_success_logs_blocking_feed_storage(self):
|
async def test_multiple_feeds_success_logs_blocking_feed_storage(self):
|
||||||
settings = {
|
settings = {
|
||||||
|
|
@ -1834,7 +1834,6 @@ class TestFeedExport(TestFeedExportBase):
|
||||||
assert not Storage.file_was_closed
|
assert not Storage.file_was_closed
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestFeedPostProcessedExports(TestFeedExportBase):
|
class TestFeedPostProcessedExports(TestFeedExportBase):
|
||||||
items = [{"foo": "bar"}]
|
items = [{"foo": "bar"}]
|
||||||
expected = b"foo\r\nbar\r\n"
|
expected = b"foo\r\nbar\r\n"
|
||||||
|
|
@ -2353,7 +2352,6 @@ class TestFeedPostProcessedExports(TestFeedExportBase):
|
||||||
assert result == expected
|
assert result == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestBatchDeliveries(TestFeedExportBase):
|
class TestBatchDeliveries(TestFeedExportBase):
|
||||||
_file_mark = "_%(batch_time)s_#%(batch_id)02d_"
|
_file_mark = "_%(batch_time)s_#%(batch_id)02d_"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,6 @@ class DropSomeItemsPipeline:
|
||||||
self.drop = True
|
self.drop = True
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestShowOrSkipMessages:
|
class TestShowOrSkipMessages:
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_class(cls):
|
def setup_class(cls):
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,6 @@ class RedirectedMediaDownloadSpider(MediaDownloadSpider):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestFileDownloadCrawl:
|
class TestFileDownloadCrawl:
|
||||||
pipeline_class = "scrapy.pipelines.files.FilesPipeline"
|
pipeline_class = "scrapy.pipelines.files.FilesPipeline"
|
||||||
store_setting_key = "FILES_STORE"
|
store_setting_key = "FILES_STORE"
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,6 @@ class ItemSpider(Spider):
|
||||||
return {"field": 42}
|
return {"field": 42}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestPipeline:
|
class TestPipeline:
|
||||||
def _on_item_scraped(self, item):
|
def _on_item_scraped(self, item):
|
||||||
assert isinstance(item, dict)
|
assert isinstance(item, dict)
|
||||||
|
|
@ -266,7 +265,6 @@ class TestCustomPipelineManager:
|
||||||
):
|
):
|
||||||
itemproc.process_item({}, crawler.spider)
|
itemproc.process_item({}, crawler.spider)
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_integration_recommended(self, mockserver: MockServer) -> None:
|
async def test_integration_recommended(self, mockserver: MockServer) -> None:
|
||||||
class CustomPipelineManager(ItemPipelineManager):
|
class CustomPipelineManager(ItemPipelineManager):
|
||||||
|
|
@ -293,7 +291,6 @@ class TestCustomPipelineManager:
|
||||||
|
|
||||||
assert len(items) == 1
|
assert len(items) == 1
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_integration_no_async_subclass(self, mockserver: MockServer) -> None:
|
async def test_integration_no_async_subclass(self, mockserver: MockServer) -> None:
|
||||||
class CustomPipelineManager(ItemPipelineManager):
|
class CustomPipelineManager(ItemPipelineManager):
|
||||||
|
|
@ -352,7 +349,6 @@ class TestCustomPipelineManager:
|
||||||
|
|
||||||
assert len(items) == 1
|
assert len(items) == 1
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@coroutine_test
|
@coroutine_test
|
||||||
async def test_integration_no_async_not_subclass(
|
async def test_integration_no_async_not_subclass(
|
||||||
self, mockserver: MockServer
|
self, mockserver: MockServer
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,6 @@ def _wrong_credentials(proxy_url):
|
||||||
return urlunsplit(bad_auth_proxy)
|
return urlunsplit(bad_auth_proxy)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@pytest.mark.requires_mitmproxy
|
@pytest.mark.requires_mitmproxy
|
||||||
class TestProxyConnect:
|
class TestProxyConnect:
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import pytest
|
|
||||||
from testfixtures import LogCapture
|
from testfixtures import LogCapture
|
||||||
|
|
||||||
from scrapy import Request, signals
|
from scrapy import Request, signals
|
||||||
|
|
@ -63,7 +62,6 @@ class AlternativeCallbacksMiddleware:
|
||||||
return response.replace(request=new_request)
|
return response.replace(request=new_request)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestCrawl:
|
class TestCrawl:
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_class(cls):
|
def setup_class(cls):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import pytest
|
|
||||||
from testfixtures import LogCapture
|
from testfixtures import LogCapture
|
||||||
|
|
||||||
from scrapy.http import Request
|
from scrapy.http import Request
|
||||||
|
|
@ -149,7 +148,6 @@ class KeywordArgumentsSpider(MockServerSpider):
|
||||||
self.crawler.stats.inc_value("boolean_checks", 1)
|
self.crawler.stats.inc_value("boolean_checks", 1)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestCallbackKeywordArguments:
|
class TestCallbackKeywordArguments:
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_class(cls):
|
def setup_class(cls):
|
||||||
|
|
|
||||||
|
|
@ -369,7 +369,6 @@ class TestIntegrationWithDownloaderAwareInMemory:
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@inline_callbacks_test
|
@inline_callbacks_test
|
||||||
def test_integration_downloader_aware_priority_queue(self):
|
def test_integration_downloader_aware_priority_queue(self):
|
||||||
with MockServer() as mockserver:
|
with MockServer() as mockserver:
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,6 @@ class TestSimpleScheduler(InterfaceCheckMixin):
|
||||||
assert close_result == "close"
|
assert close_result == "close"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestMinimalSchedulerCrawl:
|
class TestMinimalSchedulerCrawl:
|
||||||
scheduler_cls = MinimalScheduler
|
scheduler_cls = MinimalScheduler
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ class TestMockServer:
|
||||||
item = await get_from_asyncio_queue(item)
|
item = await get_from_asyncio_queue(item)
|
||||||
self.items.append(item)
|
self.items.append(item)
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
@pytest.mark.only_asyncio
|
@pytest.mark.only_asyncio
|
||||||
@inline_callbacks_test
|
@inline_callbacks_test
|
||||||
def test_simple_pipeline(self):
|
def test_simple_pipeline(self):
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,6 @@ class TestHttpErrorMiddlewareHandleAll:
|
||||||
mw.process_spider_input(res402)
|
mw.process_spider_input(res402)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestHttpErrorMiddlewareIntegrational:
|
class TestHttpErrorMiddlewareIntegrational:
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_class(cls):
|
def setup_class(cls):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import pytest
|
|
||||||
from testfixtures import LogCapture
|
from testfixtures import LogCapture
|
||||||
|
|
||||||
from scrapy import Request, Spider
|
from scrapy import Request, Spider
|
||||||
|
|
@ -319,7 +318,6 @@ class NotGeneratorOutputChainSpider(Spider):
|
||||||
|
|
||||||
|
|
||||||
# ================================================================================
|
# ================================================================================
|
||||||
@pytest.mark.requires_http_handler
|
|
||||||
class TestSpiderMiddleware:
|
class TestSpiderMiddleware:
|
||||||
mockserver: MockServer
|
mockserver: MockServer
|
||||||
|
|
||||||
|
|
|
||||||
2
tox.ini
2
tox.ini
|
|
@ -168,6 +168,7 @@ commands =
|
||||||
[testenv:no-reactor]
|
[testenv:no-reactor]
|
||||||
deps =
|
deps =
|
||||||
{[testenv]deps}
|
{[testenv]deps}
|
||||||
|
httpx
|
||||||
pytest-asyncio
|
pytest-asyncio
|
||||||
commands =
|
commands =
|
||||||
{[testenv]commands} -p no:twisted --reactor=none
|
{[testenv]commands} -p no:twisted --reactor=none
|
||||||
|
|
@ -183,6 +184,7 @@ setenv =
|
||||||
basepython = {[pinned]basepython}
|
basepython = {[pinned]basepython}
|
||||||
deps =
|
deps =
|
||||||
{[testenv:pinned]deps}
|
{[testenv:pinned]deps}
|
||||||
|
httpx==0.26.0
|
||||||
pytest-asyncio
|
pytest-asyncio
|
||||||
commands = {[pinned]commands} -p no:twisted --reactor=none
|
commands = {[pinned]commands} -p no:twisted --reactor=none
|
||||||
setenv =
|
setenv =
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue