diff --git a/tests/test_addons.py b/tests/test_addons.py index 675574d48..5c26e4651 100644 --- a/tests/test_addons.py +++ b/tests/test_addons.py @@ -7,7 +7,7 @@ from scrapy.crawler import AsyncCrawlerRunner, Crawler, CrawlerRunner from scrapy.exceptions import NotConfigured from scrapy.settings import BaseSettings, Settings from scrapy.utils.test import get_crawler, get_reactor_settings -from tests.utils.decorators import inlineCallbacks +from tests.utils.decorators import inline_callbacks_test class SimpleAddon: @@ -183,7 +183,7 @@ class TestAddonManager: extra={"crawler": crawler}, ) - @inlineCallbacks + @inline_callbacks_test def test_enable_addon_in_spider(self): class MySpider(Spider): name = "myspider" diff --git a/tests/test_closespider.py b/tests/test_closespider.py index 20c311c5a..a6fd6dd19 100644 --- a/tests/test_closespider.py +++ b/tests/test_closespider.py @@ -9,7 +9,7 @@ from tests.spiders import ( MaxItemsAndRequestsSpider, SlowSpider, ) -from tests.utils.decorators import inlineCallbacks +from tests.utils.decorators import inline_callbacks_test @pytest.mark.requires_http_handler @@ -23,7 +23,7 @@ class TestCloseSpider: def teardown_class(cls): cls.mockserver.__exit__(None, None, None) - @inlineCallbacks + @inline_callbacks_test def test_closespider_itemcount(self): close_on = 5 crawler = get_crawler(ItemSpider, {"CLOSESPIDER_ITEMCOUNT": close_on}) @@ -33,7 +33,7 @@ class TestCloseSpider: itemcount = crawler.stats.get_value("item_scraped_count") assert itemcount >= close_on - @inlineCallbacks + @inline_callbacks_test def test_closespider_pagecount(self): close_on = 5 crawler = get_crawler(FollowAllSpider, {"CLOSESPIDER_PAGECOUNT": close_on}) @@ -43,7 +43,7 @@ class TestCloseSpider: pagecount = crawler.stats.get_value("response_received_count") assert pagecount >= close_on - @inlineCallbacks + @inline_callbacks_test def test_closespider_pagecount_no_item(self): close_on = 5 max_items = 5 @@ -63,7 +63,7 @@ class TestCloseSpider: itemcount = crawler.stats.get_value("item_scraped_count") assert pagecount <= close_on + itemcount - @inlineCallbacks + @inline_callbacks_test def test_closespider_pagecount_no_item_with_pagecount(self): close_on_pagecount_no_item = 5 close_on_pagecount = 20 @@ -80,7 +80,7 @@ class TestCloseSpider: pagecount = crawler.stats.get_value("response_received_count") assert pagecount < close_on_pagecount - @inlineCallbacks + @inline_callbacks_test def test_closespider_errorcount(self): close_on = 5 crawler = get_crawler(ErrorSpider, {"CLOSESPIDER_ERRORCOUNT": close_on}) @@ -92,7 +92,7 @@ class TestCloseSpider: assert crawler.stats.get_value("spider_exceptions/count") >= close_on assert errorcount >= close_on - @inlineCallbacks + @inline_callbacks_test def test_closespider_timeout(self): close_on = 0.1 crawler = get_crawler(FollowAllSpider, {"CLOSESPIDER_TIMEOUT": close_on}) @@ -102,7 +102,7 @@ class TestCloseSpider: total_seconds = crawler.stats.get_value("elapsed_time_seconds") assert total_seconds >= close_on - @inlineCallbacks + @inline_callbacks_test def test_closespider_timeout_no_item(self): timeout = 1 crawler = get_crawler(SlowSpider, {"CLOSESPIDER_TIMEOUT_NO_ITEM": timeout}) diff --git a/tests/test_contracts.py b/tests/test_contracts.py index 9dface8aa..f1678f912 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -18,7 +18,7 @@ from scrapy.spidermiddlewares.httperror import HttpError from scrapy.spiders import Spider from scrapy.utils.test import get_crawler from tests.mockserver.http import MockServer -from tests.utils.decorators import inlineCallbacks +from tests.utils.decorators import inline_callbacks_test class DemoItem(Item): @@ -502,7 +502,7 @@ class TestContractsManager: assert self.results.errors @pytest.mark.requires_http_handler - @inlineCallbacks + @inline_callbacks_test def test_same_url(self): class TestSameUrlSpider(Spider): name = "test_same_url" diff --git a/tests/test_core_downloader.py b/tests/test_core_downloader.py index c479e66c9..2a098f9f3 100644 --- a/tests/test_core_downloader.py +++ b/tests/test_core_downloader.py @@ -25,7 +25,7 @@ from scrapy.utils.spider import DefaultSpider from scrapy.utils.test import get_crawler from tests.mockserver.http_resources import PayloadResource from tests.mockserver.utils import ssl_context_factory -from tests.utils.decorators import deferred_f_from_coro_f +from tests.utils.decorators import coroutine_test if TYPE_CHECKING: from twisted.internet.defer import Deferred @@ -97,7 +97,7 @@ class TestContextFactoryBase: class TestContextFactory(TestContextFactoryBase): - @deferred_f_from_coro_f + @coroutine_test async def testPayload(self, server_url: str) -> None: s = "0123456789" * 10 crawler = get_crawler() @@ -135,7 +135,7 @@ class TestContextFactoryTLSMethod(TestContextFactoryBase): ) assert body == to_bytes(s) - @deferred_f_from_coro_f + @coroutine_test async def test_setting_default(self, server_url: str) -> None: crawler = get_crawler() settings = Settings() @@ -155,7 +155,7 @@ class TestContextFactoryTLSMethod(TestContextFactoryBase): with pytest.raises(KeyError): load_context_factory_from_settings(settings, crawler) - @deferred_f_from_coro_f + @coroutine_test async def test_setting_explicit(self, server_url: str) -> None: crawler = get_crawler() settings = Settings({"DOWNLOADER_CLIENT_TLS_METHOD": "TLSv1.2"}) @@ -163,7 +163,7 @@ class TestContextFactoryTLSMethod(TestContextFactoryBase): assert client_context_factory._ssl_method == OpenSSL.SSL.TLSv1_2_METHOD await self._assert_factory_works(server_url, client_context_factory) - @deferred_f_from_coro_f + @coroutine_test async def test_direct_from_crawler(self, server_url: str) -> None: # the setting is ignored crawler = get_crawler(settings_dict={"DOWNLOADER_CLIENT_TLS_METHOD": "bad"}) @@ -171,14 +171,14 @@ class TestContextFactoryTLSMethod(TestContextFactoryBase): assert client_context_factory._ssl_method == OpenSSL.SSL.SSLv23_METHOD await self._assert_factory_works(server_url, client_context_factory) - @deferred_f_from_coro_f + @coroutine_test async def test_direct_init(self, server_url: str) -> None: client_context_factory = ScrapyClientContextFactory(OpenSSL.SSL.TLSv1_2_METHOD) assert client_context_factory._ssl_method == OpenSSL.SSL.TLSv1_2_METHOD await self._assert_factory_works(server_url, client_context_factory) -@deferred_f_from_coro_f +@coroutine_test async def test_fetch_deprecated_spider_arg(): class CustomDownloader(Downloader): def fetch(self, request, spider): # pylint: disable=signature-differs diff --git a/tests/test_core_scraper.py b/tests/test_core_scraper.py index 249daf943..f4ef5ec5d 100644 --- a/tests/test_core_scraper.py +++ b/tests/test_core_scraper.py @@ -6,14 +6,14 @@ import pytest from scrapy.utils.test import get_crawler from tests.spiders import SimpleSpider -from tests.utils.decorators import deferred_f_from_coro_f +from tests.utils.decorators import coroutine_test if TYPE_CHECKING: from tests.mockserver.http import MockServer @pytest.mark.requires_http_handler -@deferred_f_from_coro_f +@coroutine_test async def test_scraper_exception( mockserver: MockServer, caplog: pytest.LogCaptureFixture, diff --git a/tests/test_crawl.py b/tests/test_crawl.py index 61a4d387f..b206cfffa 100644 --- a/tests/test_crawl.py +++ b/tests/test_crawl.py @@ -55,7 +55,7 @@ from tests.spiders import ( StartGoodAndBadOutput, StartItemSpider, ) -from tests.utils.decorators import deferred_f_from_coro_f, inlineCallbacks +from tests.utils.decorators import coroutine_test, inline_callbacks_test if TYPE_CHECKING: from scrapy.statscollectors import StatsCollector @@ -74,17 +74,17 @@ class TestCrawl: def teardown_class(cls): cls.mockserver.__exit__(None, None, None) - @inlineCallbacks + @inline_callbacks_test def test_follow_all(self): crawler = get_crawler(FollowAllSpider) yield crawler.crawl(mockserver=self.mockserver) assert len(crawler.spider.urls_visited) == 11 # 10 + start_url - @deferred_f_from_coro_f + @coroutine_test async def test_fixed_delay(self): await self._test_delay(total=3, delay=0.2) - @deferred_f_from_coro_f + @coroutine_test async def test_randomized_delay(self): await self._test_delay(total=3, delay=0.1, randomize=True) @@ -122,7 +122,7 @@ class TestCrawl: average = total_time / (len(times) - 1) assert average <= delay / tolerance, "test total or delay values are too small" - @inlineCallbacks + @inline_callbacks_test def test_timeout_success(self): crawler = get_crawler(DelaySpider) yield crawler.crawl(n=0.5, mockserver=self.mockserver) @@ -130,7 +130,7 @@ class TestCrawl: assert crawler.spider.t2 > 0 assert crawler.spider.t2 > crawler.spider.t1 - @inlineCallbacks + @inline_callbacks_test def test_timeout_failure(self): crawler = get_crawler(DelaySpider, {"DOWNLOAD_TIMEOUT": 0.35}) yield crawler.crawl(n=0.5, mockserver=self.mockserver) @@ -147,7 +147,7 @@ class TestCrawl: assert crawler.spider.t2_err > 0 assert crawler.spider.t2_err > crawler.spider.t1 - @inlineCallbacks + @inline_callbacks_test def test_retry_503(self): crawler = get_crawler(SimpleSpider) with LogCapture() as log: @@ -156,7 +156,7 @@ class TestCrawl: ) self._assert_retried(log) - @inlineCallbacks + @inline_callbacks_test def test_retry_conn_failed(self): crawler = get_crawler(SimpleSpider) with LogCapture() as log: @@ -165,7 +165,7 @@ class TestCrawl: ) self._assert_retried(log) - @inlineCallbacks + @inline_callbacks_test def test_retry_dns_error(self): if NON_EXISTING_RESOLVABLE: pytest.skip("Non-existing hosts are resolvable") @@ -177,7 +177,7 @@ class TestCrawl: ) self._assert_retried(log) - @inlineCallbacks + @inline_callbacks_test def test_start_bug_before_yield(self): with LogCapture("scrapy", level=logging.ERROR) as log: crawler = get_crawler(BrokenStartSpider) @@ -188,7 +188,7 @@ class TestCrawl: assert record.exc_info is not None assert record.exc_info[0] is ZeroDivisionError - @inlineCallbacks + @inline_callbacks_test def test_start_bug_yielding(self): with LogCapture("scrapy", level=logging.ERROR) as log: crawler = get_crawler(BrokenStartSpider) @@ -199,7 +199,7 @@ class TestCrawl: assert record.exc_info is not None assert record.exc_info[0] is ZeroDivisionError - @inlineCallbacks + @inline_callbacks_test def test_start_items(self): items = [] @@ -214,7 +214,7 @@ class TestCrawl: assert len(log.records) == 0 assert items == [{"name": "test item"}] - @inlineCallbacks + @inline_callbacks_test def test_start_unsupported_output(self): """Anything that is not a request is assumed to be an item, avoiding a potentially expensive call to itemadapter.is_item(), and letting @@ -235,7 +235,7 @@ class TestCrawl: assert len(items) == 3 assert not any(isinstance(item, Request) for item in items) - @inlineCallbacks + @inline_callbacks_test def test_start_dupes(self): settings = {"CONCURRENT_REQUESTS": 1} crawler = get_crawler(DuplicateStartSpider, settings) @@ -253,7 +253,7 @@ class TestCrawl: ) assert crawler.spider.visited == 3 - @inlineCallbacks + @inline_callbacks_test def test_unbounded_response(self): # Completeness of responses without Content-Length or Transfer-Encoding # can not be determined, we treat them as valid but flagged as "partial" @@ -285,7 +285,7 @@ with multiples lines ) assert str(log).count("Got response 200") == 1 - @inlineCallbacks + @inline_callbacks_test def test_retry_conn_lost(self): # connection lost after receiving data crawler = get_crawler(SimpleSpider) @@ -295,7 +295,7 @@ with multiples lines ) self._assert_retried(log) - @inlineCallbacks + @inline_callbacks_test def test_retry_conn_aborted(self): # connection lost before receiving data crawler = get_crawler(SimpleSpider) @@ -309,7 +309,7 @@ with multiples lines assert str(log).count("Retrying") == 2 assert str(log).count("Gave up retrying") == 1 - @inlineCallbacks + @inline_callbacks_test def test_referer_header(self): """Referer header is set by RefererMiddleware unless it is already set""" req0 = Request(self.mockserver.url("/echo?headers=1&body=0"), dont_filter=1) @@ -337,7 +337,7 @@ with multiples lines echo3 = json.loads(to_unicode(crawler.spider.meta["responses"][3].body)) assert echo3["headers"].get("Referer") == ["http://example.com"] - @inlineCallbacks + @inline_callbacks_test def test_engine_status(self): est = [] @@ -353,7 +353,7 @@ with multiples lines assert s["engine.spider.name"] == crawler.spider.name assert s["len(engine.scraper.slot.active)"] == 1 - @inlineCallbacks + @inline_callbacks_test def test_format_engine_status(self): est = [] @@ -376,7 +376,7 @@ with multiples lines assert s["engine.spider.name"] == crawler.spider.name assert s["len(engine.scraper.slot.active)"] == "1" - @inlineCallbacks + @inline_callbacks_test def test_open_spider_error_on_faulty_pipeline(self): settings = { "ITEM_PIPELINES": { @@ -390,7 +390,7 @@ with multiples lines ) assert not crawler.crawling - @inlineCallbacks + @inline_callbacks_test def test_crawlerrunner_accepts_crawler(self): crawler = get_crawler(SimpleSpider) runner = CrawlerRunner() @@ -402,7 +402,7 @@ with multiples lines ) assert "Got response 200" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_crawl_multiple(self, caplog: pytest.LogCaptureFixture): runner = CrawlerRunner(get_reactor_settings()) runner.crawl( @@ -422,7 +422,7 @@ with multiples lines self._assert_retried(caplog.text) assert "Got response 200" in caplog.text - @deferred_f_from_coro_f + @coroutine_test async def test_unknown_url_scheme(self, caplog: pytest.LogCaptureFixture) -> None: crawler = get_crawler(SimpleSpider) await maybe_deferred_to_future(crawler.crawl("foo://bar")) @@ -461,7 +461,7 @@ class TestCrawlSpider: assert crawler.stats return log, items, crawler.stats - @inlineCallbacks + @inline_callbacks_test def test_crawlspider_with_parse(self): crawler = get_crawler(CrawlSpiderWithParseMethod) with LogCapture() as log: @@ -471,7 +471,7 @@ class TestCrawlSpider: assert "[parse] status 201 (foo: None)" in str(log) assert "[parse] status 202 (foo: bar)" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_crawlspider_with_async_callback(self): crawler = get_crawler(CrawlSpiderWithAsyncCallback) with LogCapture() as log: @@ -481,7 +481,7 @@ class TestCrawlSpider: assert "[parse_async] status 201 (foo: None)" in str(log) assert "[parse_async] status 202 (foo: bar)" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_crawlspider_with_async_generator_callback(self): crawler = get_crawler(CrawlSpiderWithAsyncGeneratorCallback) with LogCapture() as log: @@ -491,7 +491,7 @@ class TestCrawlSpider: assert "[parse_async_gen] status 201 (foo: None)" in str(log) assert "[parse_async_gen] status 202 (foo: bar)" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_crawlspider_with_errback(self): crawler = get_crawler(CrawlSpiderWithErrback) with LogCapture() as log: @@ -504,7 +504,7 @@ class TestCrawlSpider: assert "[errback] status 500" in str(log) assert "[errback] status 501" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_crawlspider_process_request_cb_kwargs(self): crawler = get_crawler(CrawlSpiderWithProcessRequestCallbackKeywordArguments) with LogCapture() as log: @@ -514,7 +514,7 @@ class TestCrawlSpider: assert "[parse] status 201 (foo: process_request)" in str(log) assert "[parse] status 202 (foo: bar)" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_async_def_parse(self): crawler = get_crawler(AsyncDefSpider) with LogCapture() as log: @@ -524,7 +524,7 @@ class TestCrawlSpider: assert "Got response 200" in str(log) @pytest.mark.only_asyncio - @inlineCallbacks + @inline_callbacks_test def test_async_def_asyncio_parse(self): crawler = get_crawler( AsyncDefAsyncioSpider, @@ -539,7 +539,7 @@ class TestCrawlSpider: assert "Got response 200" in str(log) @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_async_def_asyncio_parse_items_list(self): log, items, _ = await self._run_spider(AsyncDefAsyncioReturnSpider) assert "Got response 200" in str(log) @@ -547,7 +547,7 @@ class TestCrawlSpider: assert {"id": 2} in items @pytest.mark.only_asyncio - @inlineCallbacks + @inline_callbacks_test def test_async_def_asyncio_parse_items_single_element(self): items = [] @@ -564,7 +564,7 @@ class TestCrawlSpider: assert {"foo": 42} in items @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_async_def_asyncgen_parse(self): log, _, stats = await self._run_spider(AsyncDefAsyncioGenSpider) assert "Got response 200" in str(log) @@ -572,7 +572,7 @@ class TestCrawlSpider: assert itemcount == 1 @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_async_def_asyncgen_parse_loop(self): log, items, stats = await self._run_spider(AsyncDefAsyncioGenLoopSpider) assert "Got response 200" in str(log) @@ -582,7 +582,7 @@ class TestCrawlSpider: assert {"foo": i} in items @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_async_def_asyncgen_parse_exc(self): log, items, stats = await self._run_spider(AsyncDefAsyncioGenExcSpider) log = str(log) @@ -594,7 +594,7 @@ class TestCrawlSpider: assert {"foo": i} in items @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_async_def_asyncgen_parse_complex(self): _, items, stats = await self._run_spider(AsyncDefAsyncioGenComplexSpider) itemcount = stats.get_value("item_scraped_count") @@ -606,30 +606,30 @@ class TestCrawlSpider: assert {"index2": i} in items @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_async_def_asyncio_parse_reqs_list(self): log, *_ = await self._run_spider(AsyncDefAsyncioReqsReturnSpider) for req_id in range(3): assert f"Got response 200, req_id {req_id}" in str(log) @pytest.mark.only_not_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_async_def_deferred_direct(self): _, items, _ = await self._run_spider(AsyncDefDeferredDirectSpider) assert items == [{"code": 200}] @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_async_def_deferred_wrapped(self): _, items, _ = await self._run_spider(AsyncDefDeferredWrappedSpider) assert items == [{"code": 200}] - @deferred_f_from_coro_f + @coroutine_test async def test_async_def_deferred_maybe_wrapped(self): _, items, _ = await self._run_spider(AsyncDefDeferredMaybeWrappedSpider) assert items == [{"code": 200}] - @inlineCallbacks + @inline_callbacks_test def test_response_ssl_certificate_none(self): crawler = get_crawler(SingleRequestSpider) url = self.mockserver.url("/echo?body=test", is_secure=False) @@ -649,7 +649,7 @@ class TestCrawlSpider: ), ], ) - @deferred_f_from_coro_f + @coroutine_test async def test_response_ssl_certificate( self, mockserver: MockServer, url: str ) -> None: @@ -675,7 +675,7 @@ class TestCrawlSpider: ), ], ) - @deferred_f_from_coro_f + @coroutine_test async def test_response_ip_address(self, mockserver: MockServer, url: str) -> None: crawler = get_crawler(SingleRequestSpider) url = mockserver.url(url) @@ -686,7 +686,7 @@ class TestCrawlSpider: assert isinstance(ip_address, IPv4Address) assert str(ip_address) == gethostbyname(expected_netloc) - @inlineCallbacks + @inline_callbacks_test def test_bytes_received_stop_download_callback(self): crawler = get_crawler(BytesReceivedCallbackSpider) yield crawler.crawl(mockserver=self.mockserver) @@ -700,7 +700,7 @@ class TestCrawlSpider: < crawler.spider.full_response_length ) - @inlineCallbacks + @inline_callbacks_test def test_bytes_received_stop_download_errback(self): crawler = get_crawler(BytesReceivedErrbackSpider) yield crawler.crawl(mockserver=self.mockserver) @@ -716,7 +716,7 @@ class TestCrawlSpider: < crawler.spider.full_response_length ) - @inlineCallbacks + @inline_callbacks_test def test_headers_received_stop_download_callback(self): crawler = get_crawler(HeadersReceivedCallbackSpider) yield crawler.crawl(mockserver=self.mockserver) @@ -726,7 +726,7 @@ class TestCrawlSpider: "headers_received" ) - @inlineCallbacks + @inline_callbacks_test def test_headers_received_stop_download_errback(self): crawler = get_crawler(HeadersReceivedErrbackSpider) yield crawler.crawl(mockserver=self.mockserver) @@ -738,7 +738,7 @@ class TestCrawlSpider: "failure" ].value.response.headers == crawler.spider.meta.get("headers_received") - @inlineCallbacks + @inline_callbacks_test def test_spider_callback_deferred_deprecated(self): def cb(response: Response) -> Any: return succeed(None) @@ -750,7 +750,7 @@ class TestCrawlSpider: ): yield crawler.crawl(seed=self.mockserver.url("/"), callback_func=cb) - @inlineCallbacks + @inline_callbacks_test def test_spider_errback(self): failures = [] @@ -767,7 +767,7 @@ class TestCrawlSpider: assert "HTTP status code is not handled or not allowed" in str(log) assert "Spider error processing" not in str(log) - @inlineCallbacks + @inline_callbacks_test def test_spider_errback_silence(self): failures = [] @@ -783,7 +783,7 @@ class TestCrawlSpider: assert "HTTP status code is not handled or not allowed" not in str(log) assert "Spider error processing" not in str(log) - @inlineCallbacks + @inline_callbacks_test def test_spider_errback_exception(self): def eb(failure: Failure) -> None: raise ValueError("foo") @@ -795,7 +795,7 @@ class TestCrawlSpider: ) assert "Spider error processing" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_spider_errback_item(self): def eb(failure: Failure) -> Any: return {"foo": "bar"} @@ -809,7 +809,7 @@ class TestCrawlSpider: assert "Spider error processing" not in str(log) assert "'item_scraped_count': 1" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_spider_errback_request(self): def eb(failure: Failure) -> Request: return Request(self.mockserver.url("/")) @@ -823,7 +823,7 @@ class TestCrawlSpider: assert "Spider error processing" not in str(log) assert "Crawled (200)" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_spider_errback_downloader_error(self): failures = [] @@ -840,7 +840,7 @@ class TestCrawlSpider: assert "Error downloading" in str(log) assert "Spider error processing" not in str(log) - @inlineCallbacks + @inline_callbacks_test def test_spider_errback_downloader_error_exception(self): def eb(failure: Failure) -> None: raise ValueError("foo") @@ -853,7 +853,7 @@ class TestCrawlSpider: assert "Error downloading" in str(log) assert "Spider error processing" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_spider_errback_downloader_error_item(self): def eb(failure: Failure) -> Any: return {"foo": "bar"} @@ -867,7 +867,7 @@ class TestCrawlSpider: assert "Spider error processing" not in str(log) assert "'item_scraped_count': 1" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_spider_errback_downloader_error_request(self): def eb(failure: Failure) -> Request: return Request(self.mockserver.url("/")) @@ -881,7 +881,7 @@ class TestCrawlSpider: assert "Spider error processing" not in str(log) assert "Crawled (200)" in str(log) - @inlineCallbacks + @inline_callbacks_test def test_spider_errback_deferred_deprecated(self): def eb(failure: Failure) -> Any: return succeed(None) @@ -895,7 +895,7 @@ class TestCrawlSpider: seed=self.mockserver.url("/status?n=400"), errback_func=eb ) - @inlineCallbacks + @inline_callbacks_test def test_raise_closespider(self): def cb(response): raise CloseSpider @@ -906,7 +906,7 @@ class TestCrawlSpider: assert "Closing spider (cancelled)" in str(log) assert "Spider error processing" not in str(log) - @inlineCallbacks + @inline_callbacks_test def test_raise_closespider_reason(self): def cb(response): raise CloseSpider("my_reason") diff --git a/tests/test_crawler.py b/tests/test_crawler.py index 8de38114c..ba33d0bbe 100644 --- a/tests/test_crawler.py +++ b/tests/test_crawler.py @@ -41,7 +41,7 @@ from scrapy.utils.spider import DefaultSpider from scrapy.utils.test import get_crawler, get_reactor_settings from tests.mockserver.http import MockServer from tests.utils import get_script_run_env -from tests.utils.decorators import deferred_f_from_coro_f, inlineCallbacks +from tests.utils.decorators import coroutine_test, inline_callbacks_test BASE_SETTINGS: dict[str, Any] = {} @@ -101,21 +101,21 @@ class TestCrawler(TestBaseCrawler): with pytest.raises(ValueError, match="spidercls argument must be a class"): Crawler(DefaultSpider()) - @inlineCallbacks + @inline_callbacks_test def test_crawler_crawl_twice_seq_unsupported(self): crawler = get_raw_crawler(NoRequestsSpider, BASE_SETTINGS) yield crawler.crawl() with pytest.raises(RuntimeError, match="more than once on the same instance"): yield crawler.crawl() - @deferred_f_from_coro_f + @coroutine_test async def test_crawler_crawl_async_twice_seq_unsupported(self): crawler = get_raw_crawler(NoRequestsSpider, BASE_SETTINGS) await crawler.crawl_async() with pytest.raises(RuntimeError, match="more than once on the same instance"): await crawler.crawl_async() - @inlineCallbacks + @inline_callbacks_test def test_crawler_crawl_twice_parallel_unsupported(self): crawler = get_raw_crawler(NoRequestsSpider, BASE_SETTINGS) d1 = crawler.crawl() @@ -125,7 +125,7 @@ class TestCrawler(TestBaseCrawler): yield d2 @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_crawler_crawl_async_twice_parallel_unsupported(self): crawler = get_raw_crawler(NoRequestsSpider, BASE_SETTINGS) t1 = asyncio.create_task(crawler.crawl_async()) @@ -172,7 +172,7 @@ class TestCrawler(TestBaseCrawler): addon = crawler.get_addon(ChildAddon) assert addon is None - @inlineCallbacks + @inline_callbacks_test def test_get_downloader_middleware(self): class ParentDownloaderMiddleware: pass @@ -235,7 +235,7 @@ class TestCrawler(TestBaseCrawler): with pytest.raises(RuntimeError): crawler.get_downloader_middleware(DefaultSpider) - @inlineCallbacks + @inline_callbacks_test def test_get_downloader_middleware_no_engine(self): class MySpider(Spider): name = "myspider" @@ -252,7 +252,7 @@ class TestCrawler(TestBaseCrawler): with pytest.raises(RuntimeError): yield crawler.crawl() - @inlineCallbacks + @inline_callbacks_test def test_get_extension(self): class ParentExtension: pass @@ -315,7 +315,7 @@ class TestCrawler(TestBaseCrawler): with pytest.raises(RuntimeError): crawler.get_extension(DefaultSpider) - @inlineCallbacks + @inline_callbacks_test def test_get_extension_no_engine(self): class MySpider(Spider): name = "myspider" @@ -332,7 +332,7 @@ class TestCrawler(TestBaseCrawler): with pytest.raises(RuntimeError): yield crawler.crawl() - @inlineCallbacks + @inline_callbacks_test def test_get_item_pipeline(self): class ParentItemPipeline: pass @@ -395,7 +395,7 @@ class TestCrawler(TestBaseCrawler): with pytest.raises(RuntimeError): crawler.get_item_pipeline(DefaultSpider) - @inlineCallbacks + @inline_callbacks_test def test_get_item_pipeline_no_engine(self): class MySpider(Spider): name = "myspider" @@ -412,7 +412,7 @@ class TestCrawler(TestBaseCrawler): with pytest.raises(RuntimeError): yield crawler.crawl() - @inlineCallbacks + @inline_callbacks_test def test_get_spider_middleware(self): class ParentSpiderMiddleware: pass @@ -475,7 +475,7 @@ class TestCrawler(TestBaseCrawler): with pytest.raises(RuntimeError): crawler.get_spider_middleware(DefaultSpider) - @inlineCallbacks + @inline_callbacks_test def test_get_spider_middleware_no_engine(self): class MySpider(Spider): name = "myspider" @@ -516,7 +516,7 @@ class TestCrawlerLogging: get_crawler(MySpider) assert get_scrapy_root_handler() is None - @deferred_f_from_coro_f + @coroutine_test async def test_spider_custom_settings_log_level(self, tmp_path): log_file = Path(tmp_path, "log.txt") log_file.write_text("previous message\n", encoding="utf-8") @@ -686,20 +686,20 @@ class TestCrawlerRunnerHasSpider: def _crawl(runner, spider): return runner.crawl(spider) - @inlineCallbacks + @inline_callbacks_test def test_crawler_runner_bootstrap_successful(self): runner = self._runner() yield self._crawl(runner, NoRequestsSpider) assert not runner.bootstrap_failed - @inlineCallbacks + @inline_callbacks_test def test_crawler_runner_bootstrap_successful_for_several(self): runner = self._runner() yield self._crawl(runner, NoRequestsSpider) yield self._crawl(runner, NoRequestsSpider) assert not runner.bootstrap_failed - @inlineCallbacks + @inline_callbacks_test def test_crawler_runner_bootstrap_failed(self): runner = self._runner() @@ -712,7 +712,7 @@ class TestCrawlerRunnerHasSpider: assert runner.bootstrap_failed - @inlineCallbacks + @inline_callbacks_test def test_crawler_runner_bootstrap_failed_for_several(self): runner = self._runner() @@ -727,7 +727,7 @@ class TestCrawlerRunnerHasSpider: assert runner.bootstrap_failed - @inlineCallbacks + @inline_callbacks_test def test_crawler_runner_asyncio_enabled_true( self, reactor_pytest: str ) -> Generator[Deferred[Any], Any, None]: @@ -959,7 +959,7 @@ class TestCrawlerProcessSubprocessBase(ScriptRunnerMixin): p.expect_exact("Spider closed (shutdown)") p.wait() - @inlineCallbacks + @inline_callbacks_test def test_shutdown_forced(self): sig = signal.SIGINT if sys.platform != "win32" else signal.SIGBREAK args = self.get_script_args("sleeping.py", "10") @@ -1297,7 +1297,7 @@ def test_log_scrapy_info(settings, items, caplog): assert re.search(r"^Versions:\n{'" + expected_items_pattern + "'}$", version_string) -@deferred_f_from_coro_f +@coroutine_test async def test_deprecated_crawler_stop() -> None: crawler = get_crawler(DefaultSpider) d = crawler.crawl() diff --git a/tests/test_downloader_handlers.py b/tests/test_downloader_handlers.py index 7844415f5..4589e667f 100644 --- a/tests/test_downloader_handlers.py +++ b/tests/test_downloader_handlers.py @@ -21,7 +21,7 @@ from scrapy.responsetypes import responsetypes from scrapy.utils.boto import is_botocore_available from scrapy.utils.misc import build_from_crawler from scrapy.utils.test import get_crawler -from tests.utils.decorators import deferred_f_from_coro_f +from tests.utils.decorators import coroutine_test class DummyDH: @@ -126,7 +126,7 @@ class TestFile: os.close(self.fd) Path(self.tmpname).unlink() - @deferred_f_from_coro_f + @coroutine_test async def test_download(self): request = Request(path_to_file_uri(self.tmpname)) assert request.url.upper().endswith("%5E") @@ -136,7 +136,7 @@ class TestFile: assert response.body == b"0123456789" assert response.protocol is None - @deferred_f_from_coro_f + @coroutine_test async def test_non_existent(self): request = Request(path_to_file_uri(mkdtemp())) # the specific exception differs between platforms @@ -163,7 +163,7 @@ class TestS3Anon: self.s3reqh = build_from_crawler(S3DownloadHandler, crawler) self.download_request = self.s3reqh.download_request - @deferred_f_from_coro_f + @coroutine_test async def test_anon_request(self): req = Request("s3://aws-publicdatasets/") httpreq = await self.download_request(req) @@ -205,7 +205,7 @@ class TestS3: mock_formatdate.return_value = date yield - @deferred_f_from_coro_f + @coroutine_test async def test_request_signing1(self): # gets an object from the johnsmith bucket. date = "Tue, 27 Mar 2007 19:36:42 +0000" @@ -217,7 +217,7 @@ class TestS3: == b"AWS 0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbA=" ) - @deferred_f_from_coro_f + @coroutine_test async def test_request_signing2(self): # puts an object into the johnsmith bucket. date = "Tue, 27 Mar 2007 21:15:45 +0000" @@ -237,7 +237,7 @@ class TestS3: == b"AWS 0PN5J17HBGZHT7JJ3X82:hcicpDDvL9SsO6AkvxqmIWkmOuQ=" ) - @deferred_f_from_coro_f + @coroutine_test async def test_request_signing3(self): # lists the content of the johnsmith bucket. date = "Tue, 27 Mar 2007 19:42:41 +0000" @@ -256,7 +256,7 @@ class TestS3: == b"AWS 0PN5J17HBGZHT7JJ3X82:jsRt/rhG+Vtp88HrYL706QhE4w4=" ) - @deferred_f_from_coro_f + @coroutine_test async def test_request_signing4(self): # fetches the access control policy sub-resource for the 'johnsmith' bucket. date = "Tue, 27 Mar 2007 19:44:46 +0000" @@ -268,7 +268,7 @@ class TestS3: == b"AWS 0PN5J17HBGZHT7JJ3X82:thdUi9VAkzhkniLj96JIrOPGi0g=" ) - @deferred_f_from_coro_f + @coroutine_test async def test_request_signing6(self): # uploads an object to a CNAME style virtual hosted bucket with metadata. date = "Tue, 27 Mar 2007 21:06:08 +0000" @@ -297,7 +297,7 @@ class TestS3: == b"AWS 0PN5J17HBGZHT7JJ3X82:C0FlOtU8Ylb9KDTpZqYkZPX91iI=" ) - @deferred_f_from_coro_f + @coroutine_test async def test_request_signing7(self): # ensure that spaces are quoted properly before signing date = "Tue, 27 Mar 2007 19:42:41 +0000" @@ -327,7 +327,7 @@ class TestDataURI: download_handler = build_from_crawler(DataURIDownloadHandler, crawler) self.download_request = download_handler.download_request - @deferred_f_from_coro_f + @coroutine_test async def test_response_attrs(self): uri = "data:,A%20brief%20note" request = Request(uri) @@ -335,7 +335,7 @@ class TestDataURI: assert response.url == uri assert not response.headers - @deferred_f_from_coro_f + @coroutine_test async def test_default_mediatype_encoding(self): request = Request("data:,A%20brief%20note") response = await self.download_request(request) @@ -343,7 +343,7 @@ class TestDataURI: assert type(response) is responsetypes.from_mimetype("text/plain") # pylint: disable=unidiomatic-typecheck assert response.encoding == "US-ASCII" - @deferred_f_from_coro_f + @coroutine_test async def test_default_mediatype(self): request = Request("data:;charset=iso-8859-7,%be%d3%be") response = await self.download_request(request) @@ -351,7 +351,7 @@ class TestDataURI: assert type(response) is responsetypes.from_mimetype("text/plain") # pylint: disable=unidiomatic-typecheck assert response.encoding == "iso-8859-7" - @deferred_f_from_coro_f + @coroutine_test async def test_text_charset(self): request = Request("data:text/plain;charset=iso-8859-7,%be%d3%be") response = await self.download_request(request) @@ -359,7 +359,7 @@ class TestDataURI: assert response.body == b"\xbe\xd3\xbe" assert response.encoding == "iso-8859-7" - @deferred_f_from_coro_f + @coroutine_test async def test_mediatype_parameters(self): request = Request( "data:text/plain;foo=%22foo;bar%5C%22%22;" @@ -371,13 +371,13 @@ class TestDataURI: assert type(response) is responsetypes.from_mimetype("text/plain") # pylint: disable=unidiomatic-typecheck assert response.encoding == "utf-8" - @deferred_f_from_coro_f + @coroutine_test async def test_base64(self): request = Request("data:text/plain;base64,SGVsbG8sIHdvcmxkLg%3D%3D") response = await self.download_request(request) assert response.text == "Hello, world." - @deferred_f_from_coro_f + @coroutine_test async def test_protocol(self): request = Request("data:,") response = await self.download_request(request) diff --git a/tests/test_downloader_handlers_http_base.py b/tests/test_downloader_handlers_http_base.py index 4d4b4ecaa..ff76c88fe 100644 --- a/tests/test_downloader_handlers_http_base.py +++ b/tests/test_downloader_handlers_http_base.py @@ -34,7 +34,7 @@ from tests import NON_EXISTING_RESOLVABLE from tests.mockserver.proxy_echo import ProxyEchoMockServer from tests.mockserver.simple_https import SimpleMockServer from tests.spiders import SingleRequestSpider -from tests.utils.decorators import deferred_f_from_coro_f +from tests.utils.decorators import coroutine_test if TYPE_CHECKING: from collections.abc import AsyncGenerator, Generator @@ -63,21 +63,21 @@ class TestHttpBase(ABC): finally: await dh.close() - @deferred_f_from_coro_f + @coroutine_test async def test_unsupported_scheme(self) -> None: request = Request("ftp://unsupported.scheme") async with self.get_dh() as download_handler: with pytest.raises(UnsupportedURLSchemeError): await download_handler.download_request(request) - @deferred_f_from_coro_f + @coroutine_test async def test_download(self, mockserver: MockServer) -> None: request = Request(mockserver.url("/text", is_secure=self.is_secure)) async with self.get_dh() as download_handler: response = await download_handler.download_request(request) assert response.body == b"Works" - @deferred_f_from_coro_f + @coroutine_test async def test_download_head(self, mockserver: MockServer) -> None: request = Request( mockserver.url("/text", is_secure=self.is_secure), method="HEAD" @@ -94,7 +94,7 @@ class TestHttpBase(ABC): if http_status.value == 200 or http_status.value // 100 in (4, 5) ], ) - @deferred_f_from_coro_f + @coroutine_test async def test_download_has_correct_http_status_code( self, mockserver: MockServer, http_status: HTTPStatus ) -> None: @@ -105,7 +105,7 @@ class TestHttpBase(ABC): response = await download_handler.download_request(request) assert response.status == http_status.value - @deferred_f_from_coro_f + @coroutine_test async def test_server_receives_correct_request_headers( self, mockserver: MockServer ) -> None: @@ -132,7 +132,7 @@ class TestHttpBase(ABC): assert header_name in body["headers"] assert body["headers"][header_name] == [header_value] - @deferred_f_from_coro_f + @coroutine_test async def test_request_header_none(self, mockserver: MockServer) -> None: """Adding a header with None as the value should not send that header.""" request_headers = { @@ -158,7 +158,7 @@ class TestHttpBase(ABC): [("X-Custom-Header", "foo"), ("X-Custom-Header", "bar")], ], ) - @deferred_f_from_coro_f + @coroutine_test async def test_request_header_duplicate( self, mockserver: MockServer, request_headers: Any ) -> None: @@ -174,7 +174,7 @@ class TestHttpBase(ABC): assert "headers" in body assert body["headers"]["X-Custom-Header"] == ["foo", "bar"] - @deferred_f_from_coro_f + @coroutine_test async def test_server_receives_correct_request_body( self, mockserver: MockServer ) -> None: @@ -191,7 +191,7 @@ class TestHttpBase(ABC): body = json.loads(response.body.decode("utf-8")) assert json.loads(body["body"]) == request_body - @deferred_f_from_coro_f + @coroutine_test async def test_download_has_correct_response_headers( self, mockserver: MockServer ) -> None: @@ -230,14 +230,14 @@ class TestHttpBase(ABC): header_value, encoding="utf-8" ) - @deferred_f_from_coro_f + @coroutine_test async def test_redirect_status(self, mockserver: MockServer) -> None: request = Request(mockserver.url("/redirect", is_secure=self.is_secure)) async with self.get_dh() as download_handler: response = await download_handler.download_request(request) assert response.status == 302 - @deferred_f_from_coro_f + @coroutine_test async def test_redirect_status_head(self, mockserver: MockServer) -> None: request = Request( mockserver.url("/redirect", is_secure=self.is_secure), method="HEAD" @@ -246,7 +246,7 @@ class TestHttpBase(ABC): response = await download_handler.download_request(request) assert response.status == 302 - @deferred_f_from_coro_f + @coroutine_test async def test_timeout_download_from_spider_nodata_rcvd( self, mockserver: MockServer, reactor_pytest: str ) -> None: @@ -264,7 +264,7 @@ class TestHttpBase(ABC): with pytest.raises(DownloadTimeoutError): await maybe_deferred_to_future(d) - @deferred_f_from_coro_f + @coroutine_test async def test_timeout_download_from_spider_server_hangs( self, mockserver: MockServer, @@ -286,7 +286,7 @@ class TestHttpBase(ABC): await maybe_deferred_to_future(d) @pytest.mark.parametrize("send_header", [True, False]) - @deferred_f_from_coro_f + @coroutine_test async def test_host_header(self, send_header: bool, mockserver: MockServer) -> None: host_port = f"{mockserver.host}:{mockserver.port(is_secure=self.is_secure)}" request = Request( @@ -301,7 +301,7 @@ class TestHttpBase(ABC): else: assert not request.headers - @deferred_f_from_coro_f + @coroutine_test async def test_content_length_zero_bodyless_post_request_headers( self, mockserver: MockServer ) -> None: @@ -322,7 +322,7 @@ class TestHttpBase(ABC): response = await download_handler.download_request(request) assert response.body == b"0" - @deferred_f_from_coro_f + @coroutine_test async def test_content_length_zero_bodyless_post_only_one( self, mockserver: MockServer ) -> None: @@ -336,7 +336,7 @@ class TestHttpBase(ABC): assert len(contentlengths) == 1 assert contentlengths == [b"0"] - @deferred_f_from_coro_f + @coroutine_test async def test_payload(self, mockserver: MockServer) -> None: body = b"1" * 100 # PayloadResource requires body length to be 100 request = Request( @@ -348,7 +348,7 @@ class TestHttpBase(ABC): response = await download_handler.download_request(request) assert response.body == body - @deferred_f_from_coro_f + @coroutine_test async def test_response_header_content_length(self, mockserver: MockServer) -> None: request = Request( mockserver.url("/text", is_secure=self.is_secure), method="GET" @@ -364,7 +364,7 @@ class TestHttpBase(ABC): ("foo", b"\n
You are being redirected
" @@ -124,7 +124,7 @@ class TestDefaults(TestManagerBase): class TestResponseFromProcessRequest(TestManagerBase): """Tests middleware returning a response from process_request.""" - @deferred_f_from_coro_f + @coroutine_test async def test_download_func_not_called(self): req = Request("http://example.com/index.html") resp = Response("http://example.com/index.html") @@ -144,7 +144,7 @@ class TestResponseFromProcessRequest(TestManagerBase): class TestResponseFromProcessException(TestManagerBase): """Tests middleware returning a response from process_exception.""" - @deferred_f_from_coro_f + @coroutine_test async def test_process_response_called(self): req = Request("http://example.com/index.html") resp = Response("http://example.com/index.html") @@ -173,7 +173,7 @@ class TestResponseFromProcessException(TestManagerBase): class TestInvalidOutput(TestManagerBase): - @deferred_f_from_coro_f + @coroutine_test async def test_invalid_process_request(self): """Invalid return value for process_request method should raise an exception""" req = Request("http://example.com/index.html") @@ -187,7 +187,7 @@ class TestInvalidOutput(TestManagerBase): with pytest.raises(_InvalidOutput): await self._download(mwman, req) - @deferred_f_from_coro_f + @coroutine_test async def test_invalid_process_response(self): """Invalid return value for process_response method should raise an exception""" req = Request("http://example.com/index.html") @@ -201,7 +201,7 @@ class TestInvalidOutput(TestManagerBase): with pytest.raises(_InvalidOutput): await self._download(mwman, req) - @deferred_f_from_coro_f + @coroutine_test async def test_invalid_process_exception(self): """Invalid return value for process_exception method should raise an exception""" req = Request("http://example.com/index.html") @@ -222,7 +222,7 @@ class TestInvalidOutput(TestManagerBase): class TestMiddlewareUsingDeferreds(TestManagerBase): """Middlewares using Deferreds (deprecated) should work""" - @deferred_f_from_coro_f + @coroutine_test async def test_deferred(self): req = Request("http://example.com/index.html") resp = Response("http://example.com/index.html") @@ -252,7 +252,7 @@ class TestMiddlewareUsingDeferreds(TestManagerBase): class TestMiddlewareUsingCoro(TestManagerBase): """Middlewares using asyncio coroutines should work""" - @deferred_f_from_coro_f + @coroutine_test async def test_asyncdef(self): req = Request("http://example.com/index.html") resp = Response("http://example.com/index.html") @@ -270,7 +270,7 @@ class TestMiddlewareUsingCoro(TestManagerBase): assert not download_func.called @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_asyncdef_asyncio(self): req = Request("http://example.com/index.html") resp = Response("http://example.com/index.html") @@ -289,7 +289,7 @@ class TestMiddlewareUsingCoro(TestManagerBase): class TestDownloadDeprecated(TestManagerBase): - @deferred_f_from_coro_f + @coroutine_test async def test_mwman_download(self): req = Request("http://example.com/index.html") resp = Response(req.url, status=200) @@ -309,7 +309,7 @@ class TestDownloadDeprecated(TestManagerBase): class TestDeprecatedSpiderArg(TestManagerBase): - @deferred_f_from_coro_f + @coroutine_test async def test_deprecated_spider_arg(self): req = Request("http://example.com/index.html") resp = Response("http://example.com/index.html") diff --git a/tests/test_downloadermiddleware_robotstxt.py b/tests/test_downloadermiddleware_robotstxt.py index 9b4b2750b..15c68779d 100644 --- a/tests/test_downloadermiddleware_robotstxt.py +++ b/tests/test_downloadermiddleware_robotstxt.py @@ -16,7 +16,7 @@ from scrapy.settings import Settings from scrapy.utils.asyncio import call_later from scrapy.utils.defer import deferred_from_coro, maybe_deferred_to_future from tests.test_robotstxt_interface import rerp_available -from tests.utils.decorators import deferred_f_from_coro_f +from tests.utils.decorators import coroutine_test if TYPE_CHECKING: from scrapy.crawler import Crawler @@ -60,7 +60,7 @@ Disallow: /some/randome/page.html crawler.engine.download_async.side_effect = return_response return crawler - @deferred_f_from_coro_f + @coroutine_test async def test_robotstxt(self): middleware = RobotsTxtMiddleware(self._get_successful_crawler()) await self.assertNotIgnored(Request("http://site.local/allowed"), middleware) @@ -74,7 +74,7 @@ Disallow: /some/randome/page.html Request("http://site.local/wiki/Käyttäjä:"), middleware ) - @deferred_f_from_coro_f + @coroutine_test async def test_robotstxt_multiple_reqs(self) -> None: middleware = RobotsTxtMiddleware(self._get_successful_crawler()) d1 = deferred_from_coro( @@ -86,20 +86,20 @@ Disallow: /some/randome/page.html await maybe_deferred_to_future(DeferredList([d1, d2], fireOnOneErrback=True)) @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_robotstxt_multiple_reqs_asyncio(self) -> None: middleware = RobotsTxtMiddleware(self._get_successful_crawler()) c1 = middleware.process_request(Request("http://site.local/allowed1")) c2 = middleware.process_request(Request("http://site.local/allowed2")) await asyncio.gather(c1, c2) - @deferred_f_from_coro_f + @coroutine_test async def test_robotstxt_ready_parser(self): middleware = RobotsTxtMiddleware(self._get_successful_crawler()) await self.assertNotIgnored(Request("http://site.local/allowed"), middleware) await self.assertNotIgnored(Request("http://site.local/allowed"), middleware) - @deferred_f_from_coro_f + @coroutine_test async def test_robotstxt_meta(self): middleware = RobotsTxtMiddleware(self._get_successful_crawler()) meta = {"dont_obey_robotstxt": True} @@ -128,7 +128,7 @@ Disallow: /some/randome/page.html crawler.engine.download_async.side_effect = return_response return crawler - @deferred_f_from_coro_f + @coroutine_test async def test_robotstxt_garbage(self): # garbage response should be discarded, equal 'allow all' middleware = RobotsTxtMiddleware(self._get_garbage_crawler()) @@ -150,7 +150,7 @@ Disallow: /some/randome/page.html crawler.engine.download_async.side_effect = return_response return crawler - @deferred_f_from_coro_f + @coroutine_test async def test_robotstxt_empty_response(self): # empty response should equal 'allow all' middleware = RobotsTxtMiddleware(self._get_emptybody_crawler()) @@ -158,7 +158,7 @@ Disallow: /some/randome/page.html await self.assertNotIgnored(Request("http://site.local/admin/main"), middleware) await self.assertNotIgnored(Request("http://site.local/static/"), middleware) - @deferred_f_from_coro_f + @coroutine_test async def test_robotstxt_error(self, caplog: pytest.LogCaptureFixture) -> None: self.crawler.settings.set("ROBOTSTXT_OBEY", True) err = CannotResolveHostError("Robotstxt address not found") @@ -174,7 +174,7 @@ Disallow: /some/randome/page.html await middleware.process_request(Request("http://site.local")) assert "Robotstxt address not found" in caplog.text - @deferred_f_from_coro_f + @coroutine_test async def test_robotstxt_immediate_error(self): self.crawler.settings.set("ROBOTSTXT_OBEY", True) err = CannotResolveHostError("Robotstxt address not found") @@ -187,7 +187,7 @@ Disallow: /some/randome/page.html middleware = RobotsTxtMiddleware(self.crawler) await self.assertNotIgnored(Request("http://site.local"), middleware) - @deferred_f_from_coro_f + @coroutine_test async def test_ignore_robotstxt_request(self): self.crawler.settings.set("ROBOTSTXT_OBEY", True) @@ -216,7 +216,7 @@ Disallow: /some/randome/page.html middleware.process_request_2(rp, Request("http://site.local/allowed")) rp.allowed.assert_called_once_with("http://site.local/allowed", "Examplebot") - @deferred_f_from_coro_f + @coroutine_test async def test_robotstxt_local_file(self): middleware = RobotsTxtMiddleware(self._get_emptybody_crawler()) middleware.process_request_2 = mock.MagicMock() diff --git a/tests/test_downloaderslotssettings.py b/tests/test_downloaderslotssettings.py index 7d2001e47..5ddb1cdfa 100644 --- a/tests/test_downloaderslotssettings.py +++ b/tests/test_downloaderslotssettings.py @@ -11,7 +11,7 @@ from scrapy.utils.spider import DefaultSpider from scrapy.utils.test import get_crawler from tests.mockserver.http import MockServer from tests.spiders import MetaSpider -from tests.utils.decorators import deferred_f_from_coro_f, inlineCallbacks +from tests.utils.decorators import coroutine_test, inline_callbacks_test class DownloaderSlotsSettingsTestSpider(MetaSpider): @@ -68,7 +68,7 @@ class TestCrawl: self.runner = CrawlerRunner() @pytest.mark.requires_http_handler - @inlineCallbacks + @inline_callbacks_test def test_delay(self): crawler = get_crawler(DownloaderSlotsSettingsTestSpider) yield crawler.crawl(mockserver=self.mockserver) @@ -137,7 +137,7 @@ def test_get_slot_deprecated_spider_arg(): "scrapy.pqueues.DownloaderAwarePriorityQueue", ], ) -@deferred_f_from_coro_f +@coroutine_test async def test_none_slot_with_priority_queue( mockserver: MockServer, priority_queue_class: str ) -> None: diff --git a/tests/test_engine.py b/tests/test_engine.py index 939ea0f6f..c77e3c5c9 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -35,7 +35,7 @@ from scrapy.utils.signal import disconnect_all from scrapy.utils.spider import DefaultSpider from scrapy.utils.test import get_crawler from tests import get_testdata -from tests.utils.decorators import deferred_f_from_coro_f, inlineCallbacks +from tests.utils.decorators import coroutine_test, inline_callbacks_test if TYPE_CHECKING: from scrapy.core.scheduler import Scheduler @@ -374,7 +374,7 @@ class TestEngineBase: class TestEngine(TestEngineBase): @pytest.mark.requires_http_handler - @deferred_f_from_coro_f + @coroutine_test async def test_crawler(self, mockserver: MockServer) -> None: for spider in ( MySpider, @@ -392,7 +392,7 @@ class TestEngine(TestEngineBase): self._assert_bytes_received(run) @pytest.mark.requires_http_handler - @deferred_f_from_coro_f + @coroutine_test async def test_crawler_dupefilter(self, mockserver: MockServer) -> None: run = CrawlerRun(DupeFilterSpider) await run.run(mockserver) @@ -400,14 +400,14 @@ class TestEngine(TestEngineBase): self._assert_dropped_requests(run) @pytest.mark.requires_http_handler - @deferred_f_from_coro_f + @coroutine_test async def test_crawler_itemerror(self, mockserver: MockServer) -> None: run = CrawlerRun(ItemZeroDivisionErrorSpider) await run.run(mockserver) self._assert_items_error(run) @pytest.mark.requires_http_handler - @deferred_f_from_coro_f + @coroutine_test async def test_crawler_change_close_reason_on_idle( self, mockserver: MockServer ) -> None: @@ -418,7 +418,7 @@ class TestEngine(TestEngineBase): "reason": "custom_reason", } == run.signals_caught[signals.spider_closed] - @deferred_f_from_coro_f + @coroutine_test async def test_close_downloader(self): e = ExecutionEngine(get_crawler(MySpider), lambda _: None) await e.close_async() @@ -436,7 +436,7 @@ class TestEngine(TestEngineBase): get_crawler(MySpider, {"DOWNLOADER": BadDownloader}), lambda _: None ) - @inlineCallbacks + @inline_callbacks_test def test_start_already_running_exception(self): crawler = get_crawler(DefaultSpider) crawler.spider = crawler._create_spider() @@ -449,7 +449,7 @@ class TestEngine(TestEngineBase): yield deferred_from_coro(e.stop_async()) @pytest.mark.only_asyncio - @deferred_f_from_coro_f + @coroutine_test async def test_start_already_running_exception_asyncio(self): crawler = get_crawler(DefaultSpider) crawler.spider = crawler._create_spider() @@ -460,7 +460,7 @@ class TestEngine(TestEngineBase): await asyncio.gather(e.start_async(), e.start_async()) await e.stop_async() - @inlineCallbacks + @inline_callbacks_test def test_start_request_processing_exception(self): class BadRequestFingerprinter: def fingerprint(self, request): @@ -527,7 +527,7 @@ class TestEngineDownloadAsync: async def _download(engine: ExecutionEngine, request: Request) -> Response: return await engine.download_async(request) - @deferred_f_from_coro_f + @coroutine_test async def test_download_async_success(self, engine): """Test basic successful async download of a request.""" request = Request("http://example.com") @@ -543,7 +543,7 @@ class TestEngineDownloadAsync: engine._slot.remove_request.assert_called_once_with(request) engine.downloader.fetch.assert_called_once_with(request) - @deferred_f_from_coro_f + @coroutine_test async def test_download_async_redirect(self, engine): """Test async download with a redirect request.""" original_request = Request("http://example.com") @@ -569,7 +569,7 @@ class TestEngineDownloadAsync: [call(original_request), call(redirect_request)] ) - @deferred_f_from_coro_f + @coroutine_test async def test_download_async_no_spider(self, engine): """Test async download attempt when no spider is available.""" request = Request("http://example.com") @@ -577,7 +577,7 @@ class TestEngineDownloadAsync: with pytest.raises(RuntimeError, match="No open spider to crawl:"): await self._download(engine, request) - @deferred_f_from_coro_f + @coroutine_test async def test_download_async_failure(self, engine): """Test async download when the downloader raises an exception.""" request = Request("http://example.com") @@ -647,7 +647,7 @@ class TestEngineCloseSpider: crawler.spider = crawler._create_spider() return crawler - @deferred_f_from_coro_f + @coroutine_test async def test_no_slot(self, crawler: Crawler) -> None: engine = ExecutionEngine(crawler, lambda _: None) crawler.engine = engine @@ -660,14 +660,14 @@ class TestEngineCloseSpider: engine._slot = slot await engine.close_spider_async() - @deferred_f_from_coro_f + @coroutine_test async def test_no_spider(self, crawler: Crawler) -> None: engine = ExecutionEngine(crawler, lambda _: None) with pytest.raises(RuntimeError, match="Spider not opened"): await engine.close_spider_async() engine.downloader.close() # cleanup - @deferred_f_from_coro_f + @coroutine_test async def test_exception_slot( self, crawler: Crawler, caplog: pytest.LogCaptureFixture ) -> None: @@ -679,7 +679,7 @@ class TestEngineCloseSpider: await engine.close_spider_async() assert "Slot close failure" in caplog.text - @deferred_f_from_coro_f + @coroutine_test async def test_exception_downloader( self, crawler: Crawler, caplog: pytest.LogCaptureFixture ) -> None: @@ -690,7 +690,7 @@ class TestEngineCloseSpider: await engine.close_spider_async() assert "Downloader close failure" in caplog.text - @deferred_f_from_coro_f + @coroutine_test async def test_exception_scraper( self, crawler: Crawler, caplog: pytest.LogCaptureFixture ) -> None: @@ -701,7 +701,7 @@ class TestEngineCloseSpider: await engine.close_spider_async() assert "Scraper close failure" in caplog.text - @deferred_f_from_coro_f + @coroutine_test async def test_exception_scheduler( self, crawler: Crawler, caplog: pytest.LogCaptureFixture ) -> None: @@ -713,7 +713,7 @@ class TestEngineCloseSpider: await engine.close_spider_async() assert "Scheduler close failure" in caplog.text - @deferred_f_from_coro_f + @coroutine_test async def test_exception_signal( self, crawler: Crawler, caplog: pytest.LogCaptureFixture ) -> None: @@ -731,7 +731,7 @@ class TestEngineCloseSpider: reason="cancelled", ) - @deferred_f_from_coro_f + @coroutine_test async def test_exception_stats( self, crawler: Crawler, caplog: pytest.LogCaptureFixture ) -> None: @@ -742,7 +742,7 @@ class TestEngineCloseSpider: await engine.close_spider_async() assert "Stats close failure" in caplog.text - @deferred_f_from_coro_f + @coroutine_test async def test_exception_callback( self, crawler: Crawler, caplog: pytest.LogCaptureFixture ) -> None: @@ -752,7 +752,7 @@ class TestEngineCloseSpider: await engine.close_spider_async() assert "Error running spider_closed_callback" in caplog.text - @deferred_f_from_coro_f + @coroutine_test async def test_exception_async_callback( self, crawler: Crawler, caplog: pytest.LogCaptureFixture ) -> None: diff --git a/tests/test_engine_loop.py b/tests/test_engine_loop.py index f12409029..9bdbfcc56 100644 --- a/tests/test_engine_loop.py +++ b/tests/test_engine_loop.py @@ -12,7 +12,7 @@ from scrapy.utils.defer import maybe_deferred_to_future from scrapy.utils.test import get_crawler from tests.mockserver.http import MockServer from tests.test_scheduler import MemoryScheduler -from tests.utils.decorators import deferred_f_from_coro_f +from tests.utils.decorators import coroutine_test if TYPE_CHECKING: from scrapy.http import Response @@ -28,7 +28,7 @@ async def sleep(seconds: float = 0.001) -> None: class TestMain: @pytest.mark.requires_reactor # TODO - @deferred_f_from_coro_f + @coroutine_test async def test_sleep(self): """Neither asynchronous sleeps on Spider.start() nor the equivalent on the scheduler (returning no requests while also returning True from @@ -87,7 +87,7 @@ class TestMain: expected_urls = ["data:,a", "data:,b", "data:,c", "data:,d"] assert actual_urls == expected_urls, f"{actual_urls=} != {expected_urls=}" - @deferred_f_from_coro_f + @coroutine_test async def test_close_during_start_iteration( self, caplog: pytest.LogCaptureFixture ) -> None: @@ -188,7 +188,7 @@ class TestRequestSendOrder: expected_nums = sorted(start_nums + cb_nums) assert actual_nums == expected_nums, f"{actual_nums=} != {expected_nums=}" - @deferred_f_from_coro_f + @coroutine_test async def test_default(self): """By default, callback requests take priority over start requests and are sent in order. Priority matters, but given the same priority, a @@ -228,7 +228,7 @@ class TestRequestSendOrder: parse_fn=parse, ) - @deferred_f_from_coro_f + @coroutine_test async def test_lifo_start(self): """Changing the queues of start requests to LIFO, matching the queues of non-start requests, does not cause all requests to be stored in the @@ -271,7 +271,7 @@ class TestRequestSendOrder: parse_fn=parse, ) - @deferred_f_from_coro_f + @coroutine_test async def test_shared_queues(self): """If SCHEDULER_START_*_QUEUE is falsy, start requests and other requests share the same queue, i.e. start requests are not priorized @@ -333,7 +333,7 @@ class TestRequestSendOrder: # spiders. @pytest.mark.requires_http_handler - @deferred_f_from_coro_f + @coroutine_test async def test_lazy(self): start_nums = [1, 2, 4] cb_nums = [3] diff --git a/tests/test_engine_stop_download_bytes.py b/tests/test_engine_stop_download_bytes.py index bd29e9ef6..970fffbc9 100644 --- a/tests/test_engine_stop_download_bytes.py +++ b/tests/test_engine_stop_download_bytes.py @@ -13,7 +13,7 @@ from tests.test_engine import ( MySpider, TestEngineBase, ) -from tests.utils.decorators import deferred_f_from_coro_f +from tests.utils.decorators import coroutine_test if TYPE_CHECKING: from tests.mockserver.http import MockServer @@ -27,7 +27,7 @@ class BytesReceivedCrawlerRun(CrawlerRun): class TestBytesReceivedEngine(TestEngineBase): @pytest.mark.requires_http_handler - @deferred_f_from_coro_f + @coroutine_test async def test_crawler( self, mockserver: MockServer, caplog: pytest.LogCaptureFixture ) -> None: diff --git a/tests/test_engine_stop_download_headers.py b/tests/test_engine_stop_download_headers.py index 7dbaa41d2..e0008a53a 100644 --- a/tests/test_engine_stop_download_headers.py +++ b/tests/test_engine_stop_download_headers.py @@ -13,7 +13,7 @@ from tests.test_engine import ( MySpider, TestEngineBase, ) -from tests.utils.decorators import deferred_f_from_coro_f +from tests.utils.decorators import coroutine_test if TYPE_CHECKING: from tests.mockserver.http import MockServer @@ -27,7 +27,7 @@ class HeadersReceivedCrawlerRun(CrawlerRun): class TestHeadersReceivedEngine(TestEngineBase): @pytest.mark.requires_http_handler - @deferred_f_from_coro_f + @coroutine_test async def test_crawler( self, mockserver: MockServer, caplog: pytest.LogCaptureFixture ) -> None: diff --git a/tests/test_extension_telnet.py b/tests/test_extension_telnet.py index fcb027832..a956ab184 100644 --- a/tests/test_extension_telnet.py +++ b/tests/test_extension_telnet.py @@ -4,7 +4,7 @@ from twisted.cred import credentials from scrapy.extensions.telnet import TelnetConsole from scrapy.utils.test import get_crawler -from tests.utils.decorators import inlineCallbacks +from tests.utils.decorators import inline_callbacks_test pytestmark = pytest.mark.requires_reactor @@ -23,7 +23,7 @@ class TestTelnetExtension: return console, portal - @inlineCallbacks + @inline_callbacks_test def test_bad_credentials(self): console, portal = self._get_console_and_portal() creds = credentials.UsernamePassword(b"username", b"password") @@ -32,7 +32,7 @@ class TestTelnetExtension: yield d console.stop_listening() - @inlineCallbacks + @inline_callbacks_test def test_good_credentials(self): console, portal = self._get_console_and_portal() creds = credentials.UsernamePassword( @@ -42,7 +42,7 @@ class TestTelnetExtension: yield d console.stop_listening() - @inlineCallbacks + @inline_callbacks_test def test_custom_credentials(self): settings = { "TELNETCONSOLE_USERNAME": "user", diff --git a/tests/test_feedexport.py b/tests/test_feedexport.py index c65bd9df9..3a9c37042 100644 --- a/tests/test_feedexport.py +++ b/tests/test_feedexport.py @@ -55,7 +55,7 @@ from scrapy.utils.test import get_crawler from tests.mockserver.ftp import MockFTPServer from tests.mockserver.http import MockServer from tests.spiders import ItemSpider -from tests.utils.decorators import deferred_f_from_coro_f, inlineCallbacks +from tests.utils.decorators import coroutine_test, inline_callbacks_test if TYPE_CHECKING: from collections.abc import Callable, Iterable @@ -192,7 +192,7 @@ class TestFTPFeedStorage: finally: path.unlink() - @deferred_f_from_coro_f + @coroutine_test async def test_append(self): with MockFTPServer() as ftp_server: filename = "file" @@ -202,7 +202,7 @@ class TestFTPFeedStorage: await self._store(url, b"bar", feed_options=feed_options) self._assert_stored(ftp_server.path / filename, b"foobar") - @deferred_f_from_coro_f + @coroutine_test async def test_overwrite(self): with MockFTPServer() as ftp_server: filename = "file" @@ -211,7 +211,7 @@ class TestFTPFeedStorage: await self._store(url, b"bar") self._assert_stored(ftp_server.path / filename, b"bar") - @deferred_f_from_coro_f + @coroutine_test async def test_append_active_mode(self): with MockFTPServer() as ftp_server: settings = {"FEED_STORAGE_FTP_ACTIVE": True} @@ -222,7 +222,7 @@ class TestFTPFeedStorage: await self._store(url, b"bar", feed_options=feed_options, settings=settings) self._assert_stored(ftp_server.path / filename, b"foobar") - @deferred_f_from_coro_f + @coroutine_test async def test_overwrite_active_mode(self): with MockFTPServer() as ftp_server: settings = {"FEED_STORAGE_FTP_ACTIVE": True} @@ -314,7 +314,7 @@ class TestS3FeedStorage: assert storage.access_key == "uri_key" assert storage.secret_key == "uri_secret" - @deferred_f_from_coro_f + @coroutine_test async def test_store(self): settings = { "AWS_ACCESS_KEY_ID": "access_key", @@ -455,7 +455,7 @@ class TestS3FeedStorage: assert storage.region_name == region_name assert storage.s3_client._client_config.region_name == region_name - @deferred_f_from_coro_f + @coroutine_test async def test_store_without_acl(self): storage = S3FeedStorage( "s3://mybucket/export.csv", @@ -475,7 +475,7 @@ class TestS3FeedStorage: ) assert acl is None - @deferred_f_from_coro_f + @coroutine_test async def test_store_with_acl(self): storage = S3FeedStorage( "s3://mybucket/export.csv", "access_key", "secret_key", "custom-acl" @@ -540,7 +540,7 @@ class TestGCSFeedStorage: storage = GCSFeedStorage.from_crawler(crawler, "gs://mybucket/export.csv") assert storage.acl is None - @deferred_f_from_coro_f + @coroutine_test async def test_store(self): try: from google.cloud.storage import Client # noqa: F401,PLC0415 @@ -1006,7 +1006,7 @@ class TestFeedExport(TestFeedExportBase): result = self._load_until_eof(data["marshal"], load_func=marshal.load) assert result == expected - @inlineCallbacks + @inline_callbacks_test def test_stats_file_success(self): settings = { "FEEDS": { @@ -1020,7 +1020,7 @@ class TestFeedExport(TestFeedExportBase): assert "feedexport/success_count/FileFeedStorage" in crawler.stats.get_stats() assert crawler.stats.get_value("feedexport/success_count/FileFeedStorage") == 1 - @inlineCallbacks + @inline_callbacks_test def test_stats_file_failed(self): settings = { "FEEDS": { @@ -1038,7 +1038,7 @@ class TestFeedExport(TestFeedExportBase): assert "feedexport/failed_count/FileFeedStorage" in crawler.stats.get_stats() assert crawler.stats.get_value("feedexport/failed_count/FileFeedStorage") == 1 - @inlineCallbacks + @inline_callbacks_test def test_stats_multiple_file(self): settings = { "FEEDS": { @@ -1060,7 +1060,7 @@ class TestFeedExport(TestFeedExportBase): crawler.stats.get_value("feedexport/success_count/StdoutFeedStorage") == 1 ) - @deferred_f_from_coro_f + @coroutine_test async def test_export_items(self): # feed exporters use field names from Item items = [ @@ -1074,7 +1074,7 @@ class TestFeedExport(TestFeedExportBase): header = self.MyItem.fields.keys() await self.assertExported(items, header, rows) - @deferred_f_from_coro_f + @coroutine_test async def test_export_no_items_not_store_empty(self): for fmt in ("json", "jsonlines", "xml", "csv"): settings = { @@ -1086,7 +1086,7 @@ class TestFeedExport(TestFeedExportBase): data = await self.exported_no_data(settings) assert data[fmt] is None - @deferred_f_from_coro_f + @coroutine_test async def test_start_finish_exporting_items(self): items = [ self.MyItem({"foo": "bar1", "egg": "spam1"}), @@ -1106,7 +1106,7 @@ class TestFeedExport(TestFeedExportBase): assert not listener.start_without_finish assert not listener.finish_without_start - @deferred_f_from_coro_f + @coroutine_test async def test_start_finish_exporting_no_items(self): items = [] settings = { @@ -1124,7 +1124,7 @@ class TestFeedExport(TestFeedExportBase): assert not listener.start_without_finish assert not listener.finish_without_start - @deferred_f_from_coro_f + @coroutine_test async def test_start_finish_exporting_items_exception(self): items = [ self.MyItem({"foo": "bar1", "egg": "spam1"}), @@ -1145,7 +1145,7 @@ class TestFeedExport(TestFeedExportBase): assert not listener.start_without_finish assert not listener.finish_without_start - @deferred_f_from_coro_f + @coroutine_test async def test_start_finish_exporting_no_items_exception(self): items = [] settings = { @@ -1164,7 +1164,7 @@ class TestFeedExport(TestFeedExportBase): assert not listener.start_without_finish assert not listener.finish_without_start - @deferred_f_from_coro_f + @coroutine_test async def test_export_no_items_store_empty(self): formats = ( ("json", b"[]"), @@ -1184,7 +1184,7 @@ class TestFeedExport(TestFeedExportBase): data = await self.exported_no_data(settings) assert expctd == data[fmt] - @deferred_f_from_coro_f + @coroutine_test async def test_export_no_items_multiple_feeds(self): """Make sure that `storage.store` is called for every feed.""" settings = { @@ -1202,7 +1202,7 @@ class TestFeedExport(TestFeedExportBase): assert str(log).count("Storage.store is called") == 0 - @deferred_f_from_coro_f + @coroutine_test async def test_export_multiple_item_classes(self): items = [ self.MyItem({"foo": "bar1", "egg": "spam1"}), @@ -1224,7 +1224,7 @@ class TestFeedExport(TestFeedExportBase): await self.assertExportedCsv(items, header, rows_csv) await self.assertExportedJsonLines(items, rows_jl) - @deferred_f_from_coro_f + @coroutine_test async def test_export_items_empty_field_list(self): # FEED_EXPORT_FIELDS==[] means the same as default None items = [{"foo": "bar"}] @@ -1234,7 +1234,7 @@ class TestFeedExport(TestFeedExportBase): await self.assertExportedCsv(items, header, rows) await self.assertExportedJsonLines(items, rows, settings) - @deferred_f_from_coro_f + @coroutine_test async def test_export_items_field_list(self): items = [{"foo": "bar"}] header = ["foo", "baz"] @@ -1242,7 +1242,7 @@ class TestFeedExport(TestFeedExportBase): settings = {"FEED_EXPORT_FIELDS": header} await self.assertExported(items, header, rows, settings=settings) - @deferred_f_from_coro_f + @coroutine_test async def test_export_items_comma_separated_field_list(self): items = [{"foo": "bar"}] header = ["foo", "baz"] @@ -1250,7 +1250,7 @@ class TestFeedExport(TestFeedExportBase): settings = {"FEED_EXPORT_FIELDS": ",".join(header)} await self.assertExported(items, header, rows, settings=settings) - @deferred_f_from_coro_f + @coroutine_test async def test_export_items_json_field_list(self): items = [{"foo": "bar"}] header = ["foo", "baz"] @@ -1258,7 +1258,7 @@ class TestFeedExport(TestFeedExportBase): settings = {"FEED_EXPORT_FIELDS": json.dumps(header)} await self.assertExported(items, header, rows, settings=settings) - @deferred_f_from_coro_f + @coroutine_test async def test_export_items_field_names(self): items = [{"foo": "bar"}] header = {"foo": "Foo"} @@ -1266,7 +1266,7 @@ class TestFeedExport(TestFeedExportBase): settings = {"FEED_EXPORT_FIELDS": header} await self.assertExported(items, list(header.values()), rows, settings=settings) - @deferred_f_from_coro_f + @coroutine_test async def test_export_items_dict_field_names(self): items = [{"foo": "bar"}] header = { @@ -1277,7 +1277,7 @@ class TestFeedExport(TestFeedExportBase): settings = {"FEED_EXPORT_FIELDS": header} await self.assertExported(items, ["Baz", "Foo"], rows, settings=settings) - @deferred_f_from_coro_f + @coroutine_test async def test_export_items_json_field_names(self): items = [{"foo": "bar"}] header = {"foo": "Foo"} @@ -1285,7 +1285,7 @@ class TestFeedExport(TestFeedExportBase): settings = {"FEED_EXPORT_FIELDS": json.dumps(header)} await self.assertExported(items, list(header.values()), rows, settings=settings) - @deferred_f_from_coro_f + @coroutine_test async def test_export_based_on_item_classes(self): items = [ self.MyItem({"foo": "bar1", "egg": "spam1"}), @@ -1331,7 +1331,7 @@ class TestFeedExport(TestFeedExportBase): for fmt, expected in formats.items(): assert data[fmt] == expected - @deferred_f_from_coro_f + @coroutine_test async def test_export_based_on_custom_filters(self): items = [ self.MyItem({"foo": "bar1", "egg": "spam1"}), @@ -1390,7 +1390,7 @@ class TestFeedExport(TestFeedExportBase): for fmt, expected in formats.items(): assert data[fmt] == expected - @deferred_f_from_coro_f + @coroutine_test async def test_export_dicts(self): # When dicts are used, only keys from the first row are used as # a header for CSV, and all fields are used for JSON Lines. @@ -1403,7 +1403,7 @@ class TestFeedExport(TestFeedExportBase): await self.assertExportedCsv(items, ["foo", "egg"], rows_csv) await self.assertExportedJsonLines(items, rows_jl) - @deferred_f_from_coro_f + @coroutine_test async def test_export_tuple(self): items = [ {"foo": "bar1", "egg": "spam1"}, @@ -1414,7 +1414,7 @@ class TestFeedExport(TestFeedExportBase): rows = [{"foo": "bar1", "baz": ""}, {"foo": "bar2", "baz": "quux"}] await self.assertExported(items, ["foo", "baz"], rows, settings=settings) - @deferred_f_from_coro_f + @coroutine_test async def test_export_feed_export_fields(self): # FEED_EXPORT_FIELDS option allows to order export fields # and to select a subset of fields to export, both for Items and dicts. @@ -1440,7 +1440,7 @@ class TestFeedExport(TestFeedExportBase): rows = [{"egg": "spam1", "baz": ""}, {"egg": "spam2", "baz": "quux2"}] await self.assertExported(items, ["egg", "baz"], rows, settings=settings) - @deferred_f_from_coro_f + @coroutine_test async def test_export_encoding(self): items = [{"foo": "Test\xd6"}] @@ -1485,7 +1485,7 @@ class TestFeedExport(TestFeedExportBase): data = await self.exported_data(items, settings) assert data[fmt] == expected - @deferred_f_from_coro_f + @coroutine_test async def test_export_multiple_configs(self): items = [{"foo": "FOO", "bar": "BAR"}] @@ -1525,7 +1525,7 @@ class TestFeedExport(TestFeedExportBase): for fmt, expected in formats.items(): assert data[fmt] == expected - @deferred_f_from_coro_f + @coroutine_test async def test_export_indentation(self): items = [ {"foo": ["bar"]}, @@ -1681,7 +1681,7 @@ class TestFeedExport(TestFeedExportBase): data = await self.exported_data(items, settings) assert data[row["format"]] == row["expected"] - @deferred_f_from_coro_f + @coroutine_test async def test_init_exporters_storages_with_crawler(self): settings = { "FEED_EXPORTERS": {"csv": FromCrawlerCsvItemExporter}, @@ -1694,7 +1694,7 @@ class TestFeedExport(TestFeedExportBase): assert FromCrawlerCsvItemExporter.init_with_crawler assert FromCrawlerFileFeedStorage.init_with_crawler - @deferred_f_from_coro_f + @coroutine_test async def test_str_uri(self): settings = { "FEED_STORE_EMPTY": True, @@ -1703,7 +1703,7 @@ class TestFeedExport(TestFeedExportBase): data = await self.exported_no_data(settings) assert data["csv"] == b"" - @deferred_f_from_coro_f + @coroutine_test async def test_multiple_feeds_success_logs_blocking_feed_storage(self): settings = { "FEEDS": { @@ -1723,7 +1723,7 @@ class TestFeedExport(TestFeedExportBase): for fmt in ["json", "xml", "csv"]: assert f"Stored {fmt} feed (2 items)" in str(log) - @deferred_f_from_coro_f + @coroutine_test async def test_multiple_feeds_failing_logs_blocking_feed_storage(self): settings = { "FEEDS": { @@ -1743,7 +1743,7 @@ class TestFeedExport(TestFeedExportBase): for fmt in ["json", "xml", "csv"]: assert f"Error storing {fmt} feed (2 items)" in str(log) - @deferred_f_from_coro_f + @coroutine_test async def test_extend_kwargs(self): items = [{"foo": "FOO", "bar": "BAR"}] @@ -1780,7 +1780,7 @@ class TestFeedExport(TestFeedExportBase): data = await self.exported_data(items, settings) assert data[feed_options["format"]] == row["expected"] - @deferred_f_from_coro_f + @coroutine_test async def test_storage_file_no_postprocessing(self): @implementer(IFeedStorage) class Storage: @@ -1802,7 +1802,7 @@ class TestFeedExport(TestFeedExportBase): await self.exported_no_data(settings) assert Storage.open_file is Storage.store_file - @deferred_f_from_coro_f + @coroutine_test async def test_storage_file_postprocessing(self): @implementer(IFeedStorage) class Storage: @@ -1901,7 +1901,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): data_stream.seek(0) return data_stream.read() - @deferred_f_from_coro_f + @coroutine_test async def test_gzip_plugin(self): filename = self._named_tempfile("gzip_file") @@ -1920,7 +1920,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): except OSError: pytest.fail("Received invalid gzip data.") - @deferred_f_from_coro_f + @coroutine_test async def test_gzip_plugin_compresslevel(self): filename_to_compressed = { self._named_tempfile("compresslevel_0"): self.get_gzip_compressed( @@ -1957,7 +1957,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): assert compressed == data[filename] assert result == self.expected - @deferred_f_from_coro_f + @coroutine_test async def test_gzip_plugin_mtime(self): filename_to_compressed = { self._named_tempfile("mtime_123"): self.get_gzip_compressed( @@ -1992,7 +1992,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): assert compressed == data[filename] assert result == self.expected - @deferred_f_from_coro_f + @coroutine_test async def test_gzip_plugin_filename(self): filename_to_compressed = { self._named_tempfile("filename_FILE1"): self.get_gzip_compressed( @@ -2027,7 +2027,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): assert compressed == data[filename] assert result == self.expected - @deferred_f_from_coro_f + @coroutine_test async def test_lzma_plugin(self): filename = self._named_tempfile("lzma_file") @@ -2046,7 +2046,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): except lzma.LZMAError: pytest.fail("Received invalid lzma data.") - @deferred_f_from_coro_f + @coroutine_test async def test_lzma_plugin_format(self): filename_to_compressed = { self._named_tempfile("format_FORMAT_XZ"): lzma.compress( @@ -2079,7 +2079,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): assert compressed == data[filename] assert result == self.expected - @deferred_f_from_coro_f + @coroutine_test async def test_lzma_plugin_check(self): filename_to_compressed = { self._named_tempfile("check_CHECK_NONE"): lzma.compress( @@ -2112,7 +2112,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): assert compressed == data[filename] assert result == self.expected - @deferred_f_from_coro_f + @coroutine_test async def test_lzma_plugin_preset(self): filename_to_compressed = { self._named_tempfile("preset_PRESET_0"): lzma.compress( @@ -2145,7 +2145,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): assert compressed == data[filename] assert result == self.expected - @deferred_f_from_coro_f + @coroutine_test async def test_lzma_plugin_filters(self): if "PyPy" in sys.version: # https://foss.heptapod.net/pypy/pypy/-/issues/3527 @@ -2170,7 +2170,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): result = lzma.decompress(data[filename]) assert result == self.expected - @deferred_f_from_coro_f + @coroutine_test async def test_bz2_plugin(self): filename = self._named_tempfile("bz2_file") @@ -2189,7 +2189,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): except OSError: pytest.fail("Received invalid bz2 data.") - @deferred_f_from_coro_f + @coroutine_test async def test_bz2_plugin_compresslevel(self): filename_to_compressed = { self._named_tempfile("compresslevel_1"): bz2.compress( @@ -2222,7 +2222,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): assert compressed == data[filename] assert result == self.expected - @deferred_f_from_coro_f + @coroutine_test async def test_custom_plugin(self): filename = self._named_tempfile("csv_file") @@ -2238,7 +2238,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): data = await self.exported_data(self.items, settings) assert data[filename] == self.expected - @deferred_f_from_coro_f + @coroutine_test async def test_custom_plugin_with_parameter(self): expected = b"foo\r\n\nbar\r\n\n" filename = self._named_tempfile("newline") @@ -2256,7 +2256,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): data = await self.exported_data(self.items, settings) assert data[filename] == expected - @deferred_f_from_coro_f + @coroutine_test async def test_custom_plugin_with_compression(self): expected = b"foo\r\n\nbar\r\n\n" @@ -2301,7 +2301,7 @@ class TestFeedPostProcessedExports(TestFeedExportBase): result = decompressor(data[filename]) assert result == expected - @deferred_f_from_coro_f + @coroutine_test async def test_exports_compatibility_with_postproc(self): filename_to_expected = { self._named_tempfile("csv"): b"foo\r\nbar\r\n", @@ -2511,7 +2511,7 @@ class TestBatchDeliveries(TestFeedExportBase): expected_batch, rows = rows[:batch_size], rows[batch_size:] assert got_batch == expected_batch - @deferred_f_from_coro_f + @coroutine_test async def test_export_items(self): """Test partial deliveries in all supported formats""" items = [ @@ -2540,7 +2540,7 @@ class TestBatchDeliveries(TestFeedExportBase): with pytest.raises(NotConfigured): FeedExporter(crawler) - @deferred_f_from_coro_f + @coroutine_test async def test_export_no_items_not_store_empty(self): for fmt in ("json", "jsonlines", "xml", "csv"): settings = { @@ -2556,7 +2556,7 @@ class TestBatchDeliveries(TestFeedExportBase): data = dict(data) assert len(data[fmt]) == 0 - @deferred_f_from_coro_f + @coroutine_test async def test_export_no_items_store_empty(self): formats = ( ("json", b"[]"), @@ -2580,7 +2580,7 @@ class TestBatchDeliveries(TestFeedExportBase): data = dict(data) assert data[fmt][0] == expctd - @deferred_f_from_coro_f + @coroutine_test async def test_export_multiple_configs(self): items = [ {"foo": "FOO", "bar": "BAR"}, @@ -2636,7 +2636,7 @@ class TestBatchDeliveries(TestFeedExportBase): for expected_batch, got_batch in zip(expected, data[fmt], strict=False): assert got_batch == expected_batch - @deferred_f_from_coro_f + @coroutine_test async def test_batch_item_count_feeds_setting(self): items = [{"foo": "FOO"}, {"foo": "FOO1"}] formats = { @@ -2660,7 +2660,7 @@ class TestBatchDeliveries(TestFeedExportBase): for expected_batch, got_batch in zip(expected, data[fmt], strict=False): assert got_batch == expected_batch - @deferred_f_from_coro_f + @coroutine_test async def test_batch_path_differ(self): """ Test that the name of all batch files differ from each other. @@ -2682,7 +2682,7 @@ class TestBatchDeliveries(TestFeedExportBase): data = await self.exported_data(items, settings) assert len(items) == len(data["json"]) - @inlineCallbacks + @inline_callbacks_test def test_stats_batch_file_success(self): settings = { "FEEDS": { @@ -2700,7 +2700,7 @@ class TestBatchDeliveries(TestFeedExportBase): assert crawler.stats.get_value("feedexport/success_count/FileFeedStorage") == 12 @pytest.mark.requires_boto3 - @inlineCallbacks + @inline_callbacks_test def test_s3_export(self): bucket = "mybucket" items = [ @@ -2823,7 +2823,7 @@ class TestFeedExporterSignals: feed_exporter.item_scraped(item, spider) await feed_exporter.close_spider(spider) - @deferred_f_from_coro_f + @coroutine_test async def test_feed_exporter_signals_sent(self) -> None: self.feed_exporter_closed_received = False self.feed_slot_closed_received = False @@ -2835,7 +2835,7 @@ class TestFeedExporterSignals: assert self.feed_slot_closed_received assert self.feed_exporter_closed_received - @deferred_f_from_coro_f + @coroutine_test async def test_feed_exporter_signals_sent_async(self) -> None: self.feed_exporter_closed_received = False self.feed_slot_closed_received = False diff --git a/tests/test_logformatter.py b/tests/test_logformatter.py index 0602be0c8..66544a508 100644 --- a/tests/test_logformatter.py +++ b/tests/test_logformatter.py @@ -12,7 +12,7 @@ from scrapy.spiders import Spider from scrapy.utils.test import get_crawler from tests.mockserver.http import MockServer from tests.spiders import ItemSpider -from tests.utils.decorators import inlineCallbacks +from tests.utils.decorators import inline_callbacks_test class CustomItem(Item): @@ -272,7 +272,7 @@ class TestShowOrSkipMessages: }, } - @inlineCallbacks + @inline_callbacks_test def test_show_messages(self): crawler = get_crawler(ItemSpider, self.base_settings) with LogCapture() as lc: @@ -281,7 +281,7 @@ class TestShowOrSkipMessages: assert "Crawled (200)